File Coverage

blib/lib/NoZone/Zone.pm
Criterion Covered Total %
statement 238 245 97.1
branch 60 90 66.6
condition 3 7 42.8
subroutine 28 28 100.0
pod 15 15 100.0
total 344 385 89.3


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             #
3             # NoZone::Zone - record information for a bind zone
4             #
5             # Copyright (C) 2013 Daniel P. Berrange
6             #
7             # This program is free software: you can redistribute it and/or modify
8             # it under the terms of the GNU General Public License as published by
9             # the Free Software Foundation, either version 3 of the License, or
10             # (at your option) any later version.
11             #
12             # This program is distributed in the hope that it will be useful,
13             # but WITHOUT ANY WARRANTY; without even the implied warranty of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15             # GNU General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License
18             # along with this program. If not, see .
19             #
20              
21             package NoZone::Zone;
22              
23 2     2   11 use strict;
  2         5  
  2         71  
24 2     2   10 use warnings;
  2         4  
  2         62  
25              
26 2     2   1832 use POSIX qw(strftime);
  2         16849  
  2         14  
27              
28             =head1 NAME
29              
30             Nozone::Zone - record information for a bind zone
31              
32             =head1 SYNOPSIS
33              
34             use Nozone::Zone;
35              
36             my $nozone = Nozone::Zone->new(
37             domains => [
38             "nozone.org",
39             "nozone.com",
40             ],
41             hostmaster => "hostmaster",
42             lifetimes => {
43             refresh => "1H",
44             retry => "15M",
45             expire => "1W"
46             negative => "1H",
47             ttl => "1H",
48             },
49             machines => {
50             platinum => {
51             ipv4 => "12.32.56.1"
52             ipv6 => "2001:1234:6789::1"
53             },
54             gold => {
55             ipv4 => "12.32.56.2"
56             ipv6 => "2001:1234:6789::2"
57             },
58             silver => {
59             ipv4 => "12.32.56.3"
60             ipv6 => "2001:1234:6789::3"
61             },
62             },
63             default => "platinum",
64             mail => {
65             mx0 => {
66             priority => 10,
67             machine => "gold"
68             },
69             mx1 => {
70             priority => 30,
71             machine => "silver"
72             },
73             },
74             dns => {
75             ns0 => "gold",
76             ns1 => "silver",
77             },
78             names => {
79             www => "platinum",
80             },
81             aliases => {
82             db => "gold",
83             backup => "silver",
84             },
85             wildcard => "platinum",
86             inherits => $parentzone,
87             );
88              
89             foreach my $domain ($zone->get_domains()) {
90             my $conffile = "/etc/named/$domain.conf";
91             my $datafile = "/var/named/data/$domain.data";
92              
93             my $conffh = IO::File->new($conffile, ">");
94             $zone->generate_conffile($conffh, $domain, $datafile);
95             $conffh->close();
96              
97             my $datafh = IO::File->new($datafile, ">");
98             $zone->generate_datafile($datafh, $domain);
99             $datafh->close();
100             }
101              
102             =head1 DESCRIPTION
103              
104             The C class records the information for a single
105             DNS zone. A DNS zone can be associated with zero or more domain
106             names. A zone without any associated domain names can serve as
107             an abstract base from which other zones inherit data. Inheritance
108             of zones allows admins to minimize the duplication of data across
109             zones.
110              
111             A zone contains a number of parameters, which are usually provided
112             when the object is initialized.
113              
114             =over 4
115              
116             =item domains
117              
118             The C parameter is an array reference providing the list
119             of domain names associated with the DNS zone.
120              
121             domains => [
122             "nozone.org",
123             "nozone.com",
124             ]
125              
126             =item hostmaster
127              
128             The C parameter is the local part of the email address
129             of the person who manages the domain. This will be combined with
130             the domain name to form the complete email address
131              
132             hostmaster => "hostmaster"
133              
134             =item lifetimes
135              
136             The C parameter specifies various times for DNS zone
137             records. These are use to populate the SOA records in the zone.
138              
139             lifetimes => {
140             refresh => "1H",
141             retry => "15M",
142             expire => "1W"
143             negative => "1H",
144             ttl => "1H",
145             }
146              
147             =item machines
148              
149             The C parameter is a hash reference whose keys are the
150             names of physical machines. The values are further hash references
151             specifying the IPv4 and IPv6 addresses for the names.
152              
153             machines => {
154             platinum => {
155             ipv4 => "12.32.56.1"
156             ipv6 => "2001:1234:6789::1"
157             },
158             gold => {
159             ipv4 => "12.32.56.2"
160             ipv6 => "2001:1234:6789::2"
161             },
162             silver => {
163             ipv4 => "12.32.56.3"
164             ipv6 => "2001:1234:6789::3"
165             },
166             }
167              
168             =item default
169              
170             The C parameter is used to specify the name of the
171             machine which will be use as the default when resolving the
172             base domain name
173              
174             default => "platinum"
175              
176             =item mail
177              
178             The C parameter is a hash reference whose keys are the
179             names to setup as mail servers. The values are an further has
180             reference whose elements specify the priority of the mail
181             server and the name of the machine defined in the C
182             parameter.
183              
184             mail => {
185             mx0 => {
186             priority => 10,
187             machine => "gold"
188             },
189             mx1 => {
190             priority => 30,
191             machine => "silver"
192             },
193             }
194              
195             =item dns
196              
197             The C parameter is a hash reference whose keys are the
198             names to setup as DNS servers. The values are the names of
199             machines defined in the C parameter which are to
200             used to define the corresponding IP addresses
201              
202             dns => [
203             ns0 => "gold",
204             ns1 => "silver",
205             ]
206              
207             =item names
208              
209             The C parameter is a hash reference whose keys reflect
210             additional names to be defined as A/AAAA records for the zone.
211             The values refer to keys in the C parameter and are
212             used to define the corresponding IP addresses
213              
214             names => {
215             www => "platinum",
216             }
217              
218             =item aliases
219              
220             The C parameter is a has reference whose keys reflect
221             additional names to be defiend as CNAME records for the zone.
222             The values refer to keys in the C or C
223             parameter and are used to the define the CNAME target.
224              
225             aliases => {
226             db => "gold",
227             backup => "silver",
228             }
229              
230             =item wildcard
231              
232             The C parameter is a string refering to a name
233             defined in the C parameter. If set this parameter
234             is used to defined a wildcard DNS entry in the zone.
235              
236             wildcard => "platinum"
237              
238             =back
239              
240             =head1 METHODS
241              
242             =over 4
243              
244             =item my $zone = NoZone::Zone->new(%params);
245              
246             Creates a new L object to hold information
247             about a DNS zone. The C<%params> has keys are allowed to
248             be any of the parameters documented earlier in this
249             document. In addition the C parameter is valid
250             and can refer to another instance of the L
251             class.
252              
253             =cut
254              
255              
256             sub new {
257 6     6 1 1357 my $proto = shift;
258 6   33     26 my $class = ref($proto) || $proto;
259 6         10 my $self = {};
260 6         34 my %params = @_;
261              
262 6 50       22 $self->{domains} = $params{domains} ? $params{domains} : [];
263 6 100       15 $self->{hostmaster} = $params{hostmaster} ? $params{hostmaster} : "hostmaster";
264 6 100       15 $self->{lifetimes} = $params{lifetimes} ? $params{lifetimes} : undef;
265 6 50       19 $self->{machines} = $params{machines} ? $params{machines} : {};
266 6 100       19 $self->{default} = $params{default} ? $params{default} : undef;
267 6 50       16 $self->{mail} = $params{mail} ? $params{mail} : {};
268 6 50       15 $self->{dns} = $params{dns} ? $params{dns} : [];
269 6 50       23 $self->{names} = $params{names} ? $params{names} : {};
270 6 50       16 $self->{aliases} = $params{aliases} ? $params{aliases} : {};
271 6 100       14 $self->{wildcard} = $params{wildcard} ? $params{wildcard} : undef;
272 6 50       16 $self->{inherits} = $params{inherits} ? $params{inherits} : undef;
273              
274 6         11 bless $self, $class;
275              
276 6         24 return $self;
277             }
278              
279              
280             =item $zone->set_inherits($parentzone);
281              
282             Sets the zone from which this zone will inherit data
283             parameters. The C<$parentzone> method should be an
284             instance of the C class.
285              
286             =cut
287              
288              
289             sub set_inherits {
290 4     4 1 5 my $self = shift;
291 4         6 my $zone = shift;
292              
293 4         24 $self->{inherits} = $zone;
294             }
295              
296              
297             =item my @domains = $zone->get_domains();
298              
299             Returns the array of domain names associated directly
300             with this zone.
301              
302             =cut
303              
304             sub get_domains {
305 6     6 1 9 my $self = shift;
306              
307 6         6 return @{$self->{domains}};
  6         28  
308             }
309              
310              
311             =item my $name = $zone->get_hostmaster();
312              
313             Returns the hostmaster setting associated with this
314             zone, if any. If no hostmaster is set against this zone,
315             then the hostmaster from any parent zone will be returned.
316             If no parent zone is present, an undefined value will
317             be returned.
318              
319             =cut
320              
321             sub get_hostmaster {
322 3     3 1 4 my $self = shift;
323              
324 3 50       10 if (defined $self->{hostmaster}) {
325 3         6 return $self->{hostmaster};
326             }
327              
328 0 0       0 if (defined $self->{inherits}) {
329 0         0 return $self->{inherits}->get_hostmaster();
330             }
331              
332 0         0 return "hostmaster";
333             }
334              
335              
336             =item my %lifetimes = $zone->get_lifetimes();
337              
338             Return a hash containing the lifetimes defined against
339             this zone. If no data is defined for this zone, then
340             the data from any parent zone is returned. If not
341             parent zone is set, then some sensible default times
342             are returned.
343              
344             =cut
345              
346             sub get_lifetimes {
347 6     6 1 8 my $self = shift;
348              
349 6 100       16 if (defined $self->{lifetimes}) {
350 3         5 return %{$self->{lifetimes}};
  3         27  
351             }
352              
353 3 50       10 if ($self->{inherits}) {
354 3         9 return $self->{inherits}->get_lifetimes();
355             }
356              
357             return (
358 0         0 refresh => "1H",
359             retry => "15M",
360             expire => "1W",
361             negative => "1H",
362             ttl => "1H",
363             );
364             }
365              
366              
367             =item my %machines = $zone->get_machines();
368              
369             Return hash containing the union of all the machines
370             defined in this zone and its parent(s) recursively.
371              
372             =cut
373              
374             sub get_machines {
375 66     66 1 66 my $self = shift;
376              
377 66         65 my %machines;
378              
379 66 100       208 if ($self->{inherits}) {
380 33         64 %machines = $self->{inherits}->get_machines();
381             }
382              
383 66         93 foreach my $name (keys %{$self->{machines}}) {
  66         167  
384 99         192 $machines{$name} = $self->{machines}->{$name};
385             }
386              
387 66         217 return %machines;
388             }
389              
390              
391             =item $machine = $zone->get_machine($name);
392              
393             Return a hash reference containing the IP addresses
394             associated with the machine named C<$name>.
395              
396             =cut
397              
398             sub get_machine {
399 30     30 1 35 my $self = shift;
400 30         34 my $name = shift;
401              
402 30         126 my %machines = $self->get_machines();
403              
404 30 50       122 return exists $machines{$name} ? $machines{$name} : undef;
405             }
406              
407              
408             =item my $name = $zone->get_default();
409              
410             Returns the name of the machine to be used as the
411             default for the zone. If no default is defined
412             for this zone, then the default from any parent
413             zone is defined. If no parent zone is defined,
414             then return an undefined value
415              
416             =cut
417              
418             sub get_default {
419 6     6 1 9 my $self = shift;
420              
421 6 100       14 if (defined $self->{default}) {
422 3         8 return $self->{default};
423             }
424              
425 3 50       9 if (defined $self->{inherits}) {
426 3         8 return $self->{inherits}->get_default();
427             }
428              
429 0         0 return undef;
430             }
431              
432              
433             =item my %dns = $zone->get_dns();
434              
435             Return a hash containing the union of all the machines
436             defined as dns servers in this zone and its parent(s)
437             recursively.
438              
439             =cut
440              
441             sub get_dns {
442 6     6 1 6 my $self = shift;
443              
444 6         7 my %dns;
445              
446 6 100       14 if ($self->{inherits}) {
447 3         8 %dns = $self->{inherits}->get_dns();
448             }
449              
450 6         8 foreach my $name (keys %{$self->{dns}}) {
  6         21  
451 6         15 $dns{$name} = $self->{dns}->{$name};
452             }
453              
454 6         27 return %dns;
455             }
456              
457              
458             =item my %mail = $zone->get_mail();
459              
460             Return a hash containing the union of all the machines
461             defined as mail servers in this zone and its parent(s)
462             recursively.
463              
464             =cut
465              
466             sub get_mail {
467 6     6 1 8 my $self = shift;
468              
469 6         6 my %mail;
470              
471 6 100       15 if ($self->{inherits}) {
472 3         8 %mail = $self->{inherits}->get_mail();
473             }
474              
475 6         7 foreach my $name (keys %{$self->{mail}}) {
  6         17  
476 6         16 $mail{$name} = $self->{mail}->{$name};
477             }
478              
479 6         24 return %mail;
480             }
481              
482              
483             =item %names = $zone->get_names();
484              
485             Return a hash containing the union of all the machine
486             hostnames defined in this zone and its parent(s)
487             recursively.
488              
489             =cut
490              
491             sub get_names {
492 6     6 1 8 my $self = shift;
493              
494 6         8 my %names;
495              
496 6 100       28 if ($self->{inherits}) {
497 3         17 %names = $self->{inherits}->get_names();
498             }
499              
500 6         10 foreach my $name (keys %{$self->{names}}) {
  6         15  
501 3         11 $names{$name} = $self->{names}->{$name};
502             }
503              
504 6         20 return %names;
505             }
506              
507              
508             =item %names = $zone->get_aliases();
509              
510             Return a hash containing the union of all the machine
511             aliases defined in this zone and its parent(s)
512             recursively.
513              
514             =cut
515              
516             sub get_aliases {
517 6     6 1 8 my $self = shift;
518              
519 6         7 my %aliases;
520              
521 6 100       11 if ($self->{inherits}) {
522 3         10 %aliases = $self->{inherits}->get_aliases();
523             }
524              
525 6         9 foreach my $name (keys %{$self->{aliases}}) {
  6         16  
526 6         24 $aliases{$name} = $self->{aliases}->{$name};
527             }
528              
529 6         27 return %aliases;
530             }
531              
532              
533             =item my $name = $zone->get_wildcard();
534              
535             Return the name of the machine which will handle
536             wildcard name lookups. If no wildcard is defined
537             against the zone, returns the wildcard of the
538             parent zone. If there is no parent zone, an
539             undefined value is returned, indicating that no
540             wildcard DNS entry will be created.
541              
542             =cut
543              
544             sub get_wildcard {
545 6     6 1 8 my $self = shift;
546              
547 6 100       14 if (defined $self->{wildcard}) {
548 3         9 return $self->{wildcard};
549             }
550              
551 3 50       8 if ($self->{inherits}) {
552 3         8 return $self->{inherits}->get_wildcard();
553             }
554              
555 0         0 return undef;
556             }
557              
558             =item $zone->generate_conffile($fh, $domain, $datafile, \@masters, $verbose=0);
559              
560             Generate a Bind zone conf file for the domain C<$domain>
561             writing the data to the file handle C<$fh>. C<$fh>
562             should be an instance of L. The optional C<$verbose>
563             parameter can be set to a true value to print progress on
564             stdout. If C<@masters> is a non-empty list, then a slave
565             config will be created, otherwise a master config will be
566             created. The C<$datafile> parameter should specify the
567             path to the corresponding zone data file, usually kept
568             in C.
569              
570             =cut
571              
572             sub generate_conffile {
573 6     6 1 8 my $self = shift;
574 6         8 my $fh = shift;
575 6         8 my $domain = shift;
576 6         8 my $datafile = shift;
577 6         9 my $masters = shift;
578 6   50     15 my $verbose = shift || 0;
579              
580 6 100       6 if (int(@{$masters})) {
  6         17  
581 3         5 my $masterlist = join (" ; ", @{$masters});
  3         9  
582              
583 3         23 print $fh <
584             zone "$domain" in {
585             type slave;
586             file "$datafile";
587             masters { $masterlist ; };
588             };
589             EOF
590             } else {
591 3         138 print $fh <
592             zone "$domain" in {
593             type master;
594             file "$datafile";
595             };
596             EOF
597             }
598             }
599              
600              
601             =item $zone->generate_datafile($fh, $domain, $verbose=0);
602              
603             Generate a Bind zone data file for the domain C<$domain>
604             writing the data to the file handle C<$fh>. C<$fh>
605             should be an instance of L. The optional C<$verbose>
606             parameter can be set to a true value to print progress on
607             stdout.
608              
609             =cut
610              
611             sub generate_datafile {
612 3     3 1 5 my $self = shift;
613 3         6 my $fh = shift;
614 3         5 my $domain = shift;
615 3   50     7 my $verbose = shift || 0;
616              
617 3         10 $self->_generate_soa($fh, $domain, $verbose);
618 3         12 $self->_generate_default($fh, $verbose);
619 3         36 $self->_generate_dns($fh, $verbose);
620 3         9 $self->_generate_mail($fh, $verbose);
621 3         8 $self->_generate_machines($fh, $verbose);
622 3         14 $self->_generate_names($fh, $verbose);
623 3         8 $self->_generate_aliases($fh, $verbose);
624 3         7 $self->_generate_wildcard($fh, $verbose);
625             }
626              
627              
628             sub _generate_soa {
629 3     3   5 my $self = shift;
630 3         5 my $fh = shift;
631 3         11 my $domain = shift;
632 3         4 my $verbose = shift;
633              
634 3 50       19 print " - Generate soa $domain\n" if $verbose;
635              
636 3         10 my $hostmaster = $self->get_hostmaster();
637              
638 3         10 my $now = time;
639 3         262 my $time = strftime("%Y/%m/%d %H:%M:%S", gmtime(time));
640              
641 3         10 my %lifetimes = $self->get_lifetimes();
642 3         9 my $refresh = $lifetimes{refresh};
643 3         4 my $retry = $lifetimes{retry};
644 3         5 my $expire = $lifetimes{expire};
645 3         4 my $negative = $lifetimes{negative};
646 3         5 my $ttl = $lifetimes{ttl};
647              
648 3         47 print $fh <
649             \$ORIGIN $domain.
650             \$TTL $ttl ; queries are cached for this long
651             @ IN SOA ns1 $hostmaster (
652             $now ; Date $time
653             $refresh ; slave queries for refresh this often
654             $retry ; slave retries refresh this often after failure
655             $expire ; slave expires after this long if not refreshed
656             $negative ; errors are cached for this long
657             )
658              
659             EOF
660              
661             }
662              
663              
664             sub _generate_record {
665 78     78   85 my $self = shift;
666 78         160 my $fh = shift;
667 78         81 my $name = shift;
668 78         78 my $type = shift;
669 78         81 my $detail = shift;
670 78         77 my $value = shift;
671 78         232 my $comment = shift;
672              
673 78         86 my $suffix = "";
674 78 100       140 if (defined $comment) {
675 42         56 $suffix = " ; " . $comment;
676             }
677              
678 78         872 printf $fh "%-20s IN %-8s %-6s %s%s\n", $name, $type, $detail, $value, $suffix;
679             }
680              
681             sub _generate_machine {
682 30     30   35 my $self = shift;
683 30         35 my $fh = shift;
684 30         237 my $name = shift;
685 30         36 my $machine = shift;
686 30         28 my $verbose = shift;
687              
688 30 100       201 print " - Generate [$name] for [$machine]\n" if $verbose;
689              
690 30         125 my $addrs = $self->get_machine($machine);
691              
692 30 50       60 die "cannot find machine $machine" unless defined $addrs;
693              
694 30         26 my $comment;
695 30 100       60 if ($name ne $machine) {
696 21         24 $comment = "Machine $machine";
697             }
698              
699 30 50       106 $self->_generate_record($fh, $name, "A", "", $addrs->{ipv4}, $comment) if exists $addrs->{ipv4};
700 30 50       117 $self->_generate_record($fh, $name, "AAAA", "", $addrs->{ipv6}, $comment) if exists $addrs->{ipv6};
701             }
702              
703              
704             sub _generate_default {
705 3     3   5 my $self = shift;
706 3         4 my $fh = shift;
707 3         3 my $cfg = shift;
708 3         4 my $domain = shift;
709 3         4 my $verbose = shift;
710              
711 3 50       8 print " - Generate default\n" if $verbose;
712              
713 3         9 my $default = $self->get_default();
714              
715 3 50       7 if (defined $default) {
716 3         5 print $fh "; Primary name records for unqualfied domain\n";
717 3         10 $self->_generate_machine($fh, "\@", $default, $verbose);
718 3         7 print $fh "\n";
719             }
720             }
721              
722              
723             sub _generate_dns {
724 3     3   5 my $self = shift;
725 3         3 my $fh = shift;
726 3         4 my $verbose = shift;
727              
728 3 50       36 print " - Generate dns\n" if $verbose;
729              
730 3         8 my %dns = $self->get_dns();
731              
732 3         6 print $fh "; DNS server records\n";
733              
734 3         9 my @dns = sort { $a cmp $b } keys %dns;
  3         10  
735 3         7 foreach my $name (@dns) {
736 6         14 $self->_generate_record($fh, "\@", "NS", "", $name);
737             }
738              
739 3         7 foreach my $name (@dns) {
740 6         15 $self->_generate_machine($fh, $name, $dns{$name}, $verbose);
741             }
742 3         11 print $fh "\n";
743             }
744              
745              
746             sub _generate_mail {
747 3     3   4 my $self = shift;
748 3         4 my $fh = shift;
749 3         4 my $verbose = shift;
750              
751 3 50       20 print " - Generate mail\n" if $verbose;
752              
753 3         9 my %mail = $self->get_mail();
754              
755 3         6 print $fh "; E-Mail server records\n";
756              
757 3         9 my @mail = sort { $a cmp $b } keys %mail;
  3         10  
758 3         5 foreach my $name (@mail) {
759 6         13 my $prio = $mail{$name}->{'priority'};
760 6         19 $self->_generate_record($fh, "\@", "MX", $prio, $name);
761             }
762              
763 3         6 foreach my $name (@mail) {
764 6         11 my $machine = $mail{$name}->{'machine'};
765 6         13 $self->_generate_machine($fh, $name, $machine, $verbose);
766             }
767 3         10 print $fh "\n";
768             }
769              
770              
771             sub _generate_machines {
772 3     3   4 my $self = shift;
773 3         3 my $fh = shift;
774 3         5 my $verbose = shift;
775              
776 3 50       17 print " - Generate machines\n" if $verbose;
777              
778 3         7 my %names = $self->get_machines();
779              
780 3 50       11 if (%names) {
781 3         4 print $fh "; Primary names\n";
782              
783 3         9 foreach my $name (sort { $a cmp $b } keys %names) {
  6         13  
784 9         18 $self->_generate_machine($fh, $name, $name, $verbose);
785             }
786 3         10 print $fh "\n";
787             }
788             }
789              
790              
791             sub _generate_names {
792 3     3   4 my $self = shift;
793 3         4 my $fh = shift;
794 3         4 my $verbose = shift;
795              
796 3 50       17 print " - Generate names\n" if $verbose;
797              
798 3         8 my %names = $self->get_names();
799              
800 3 50       10 if (%names) {
801 3         5 print $fh "; Extra names\n";
802              
803 3         8 foreach my $name (sort { $a cmp $b } keys %names) {
  0         0  
804 3         9 $self->_generate_machine($fh, $name, $names{$name}, $verbose);
805             }
806 3         9 print $fh "\n";
807             }
808             }
809              
810              
811             sub _generate_aliases {
812 3     3   4 my $self = shift;
813 3         5 my $fh = shift;
814 3         4 my $verbose = shift;
815              
816 3 50       18 print " - Generate aliases\n" if $verbose;
817              
818 3         8 my %aliases = $self->get_aliases();
819              
820 3 50       8 if (%aliases) {
821 3         7 print $fh "; Aliased names\n";
822              
823 3         8 foreach my $alias (sort { $a cmp $b } keys %aliases) {
  3         9  
824 6         13 $self->_generate_record($fh, $alias, "CNAME", "", $aliases{$alias});
825             }
826 3         10 print $fh "\n";
827             }
828             }
829              
830              
831             sub _generate_wildcard {
832 3     3   5 my $self = shift;
833 3         3 my $fh = shift;
834 3         5 my $verbose = shift;
835              
836 3 50       15 print " - Generate wildcard\n" if $verbose;
837 3         7 my $wildcard = $self->get_wildcard();
838              
839 3 50       7 if (defined $wildcard) {
840 3         5 print $fh "; Wildcard\n";
841 3         7 $self->_generate_machine($fh, "*", $wildcard, $verbose);
842 3         13 print $fh "\n";
843             }
844             }
845              
846              
847             1;