| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package OpenVZ::Vzctl; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # ABSTRACT: Call OpenVZ vzctl command from your program | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | #XXX: Do we need to load and parse the VZ system config file? | 
| 6 |  |  |  |  |  |  | #XXX: Need to abstract out the common code into a top level OpenVZ module. | 
| 7 |  |  |  |  |  |  | #XXX: Need to handle version call | 
| 8 |  |  |  |  |  |  | #XXX: Need to use 'on_fail' option for validate_with for smoother error | 
| 9 |  |  |  |  |  |  | #     handling. | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 20 |  |  | 20 |  | 3871345 | use 5.006; | 
|  | 20 |  |  |  |  | 84 |  | 
|  | 20 |  |  |  |  | 882 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 | 20 |  |  | 20 |  | 119 | use strict; | 
|  | 20 |  |  |  |  | 40 |  | 
|  | 20 |  |  |  |  | 656 |  | 
| 15 | 20 |  |  | 20 |  | 105 | use warnings; | 
|  | 20 |  |  |  |  | 39 |  | 
|  | 20 |  |  |  |  | 567 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 20 |  |  | 20 |  | 17877 | use namespace::autoclean; | 
|  | 20 |  |  |  |  | 342792 |  | 
|  | 20 |  |  |  |  | 133 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 20 |  |  | 20 |  | 1614 | use Carp; | 
|  | 20 |  |  |  |  | 43 |  | 
|  | 20 |  |  |  |  | 1458 |  | 
| 20 | 20 |  |  | 20 |  | 19876 | use List::MoreUtils qw( any ); | 
|  | 20 |  |  |  |  | 82703 |  | 
|  | 20 |  |  |  |  | 1761 |  | 
| 21 | 20 |  |  | 20 |  | 16143 | use OpenVZ ':all'; | 
|  | 20 |  |  |  |  | 77 |  | 
|  | 20 |  |  |  |  | 131 |  | 
| 22 | 20 |  |  | 20 |  | 6836 | use Params::Validate ':all'; | 
|  | 20 |  |  |  |  | 45 |  | 
|  | 20 |  |  |  |  | 4910 |  | 
| 23 | 20 |  |  | 20 |  | 1093 | use Regexp::Common qw( URI net ); | 
|  | 20 |  |  |  |  | 4071 |  | 
|  | 20 |  |  |  |  | 212 |  | 
| 24 | 20 |  |  | 20 |  | 50224 | use Scalar::Util 'blessed'; | 
|  | 20 |  |  |  |  | 42 |  | 
|  | 20 |  |  |  |  | 1004 |  | 
| 25 | 20 |  |  | 20 |  | 105 | use Sub::Exporter; | 
|  | 20 |  |  |  |  | 40 |  | 
|  | 20 |  |  |  |  | 112 |  | 
| 26 |  |  |  |  |  |  |  | 
| 27 | 20 |  |  | 20 |  | 3738 | use parent 'OpenVZ'; | 
|  | 20 |  |  |  |  | 43 |  | 
|  | 20 |  |  |  |  | 241 |  | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | our $VERSION = '0.01'; # VERSION | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | our $AUTOLOAD; | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | ############################################################################ | 
| 34 |  |  |  |  |  |  | # Base structure describing the subcommands and their arguments. | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | # Every subcommand requires ctid and has the optional flag of C or C.  Though these flags are mutually exclusive, | 
| 38 |  |  |  |  |  |  | # C will accept both at the same time.  Results are undefined when using both flag at the same time.  However, this code is | 
| 39 |  |  |  |  |  |  | # setup to accept only one or the other. | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | # Surrounding a paremeter with square brackets ( [parm] ) will make the parm optional in C. | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | {  # Quick, hide in here! And don't make a *sound*! | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | my @vzctl_exports; | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | push @vzctl_exports, 'execute';  # imported from OpenVZ | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | my %vzctl = ( | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | destroy   => [], | 
| 52 |  |  |  |  |  |  | mount     => [], | 
| 53 |  |  |  |  |  |  | quotainit => [], | 
| 54 |  |  |  |  |  |  | quotaoff  => [], | 
| 55 |  |  |  |  |  |  | quotaon   => [], | 
| 56 |  |  |  |  |  |  | restart   => [], | 
| 57 |  |  |  |  |  |  | status    => [], | 
| 58 |  |  |  |  |  |  | stop      => [], | 
| 59 |  |  |  |  |  |  | umount    => [], | 
| 60 |  |  |  |  |  |  | exec      => [qw( command )], | 
| 61 |  |  |  |  |  |  | exec2     => [qw( command )], | 
| 62 |  |  |  |  |  |  | runscript => [qw( script )], | 
| 63 |  |  |  |  |  |  | start     => [qw( [force] [wait] )], | 
| 64 |  |  |  |  |  |  | enter     => [qw( [exec] )], | 
| 65 |  |  |  |  |  |  | chkpnt    => [qw( [create_dumpfile] )], | 
| 66 |  |  |  |  |  |  | restore   => [qw( [restore_dumpfile] )], | 
| 67 |  |  |  |  |  |  | create    => [qw( [config] [hostname] [ipadd] [ostemplate] [private] [root] )], | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | set => [ qw( | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | [applyconfig] [applyconfig_map] [avnumproc] [bootorder] [capability] | 
| 72 |  |  |  |  |  |  | [cpulimit] [cpumask] [cpus] [cpuunits] [dcachesize] [devices] [devnodes] | 
| 73 |  |  |  |  |  |  | [dgramrcvbuf] [disabled] [diskinodes] [diskspace] [features] [force] | 
| 74 |  |  |  |  |  |  | [hostname] [ioprio] [ipadd] [ipdel] [iptables] [kmemsize] [lockedpages] | 
| 75 |  |  |  |  |  |  | [name] [nameserver] [netif_add] [netif_del] [noatime] [numfile] | 
| 76 |  |  |  |  |  |  | [numflock] [numiptent] [numothersock] [numproc] [numpty] [numsiginfo] | 
| 77 |  |  |  |  |  |  | [numtcpsock] [onboot] [oomguarpages] [othersockbuf] [pci_add] [pci_del] | 
| 78 |  |  |  |  |  |  | [physpages] [privvmpages] [quotatime] [quotaugidlimit] [save] | 
| 79 |  |  |  |  |  |  | [searchdomain] [setmode] [shmpages] [swappages] [tcprcvbuf] [tcpsndbuf] | 
| 80 |  |  |  |  |  |  | [userpasswd] [vmguarpages] | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | ), | 
| 83 |  |  |  |  |  |  | ], | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | ); | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | #################################### | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | push @vzctl_exports, 'known_commands'; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 21 |  |  | 21 | 1 | 14065 | sub known_commands { return keys %vzctl } | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | #################################### | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | push @vzctl_exports, 'known_options'; | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | my $commands_rx = join q{|}, keys %vzctl; | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | sub known_options { ## no critic qw( Subroutines::RequireArgUnpacking ) | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | #my @spec; $spec[0] = { type => SCALAR, regex => qr/^$commands_rx$/ }; | 
| 104 | 18 |  |  | 18 | 1 | 114106 | my @spec = ( { type => SCALAR, regex => qr/^$commands_rx$/ } ); | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 18 |  |  |  |  | 711 | my @arg = validate_with( params => \@_, spec => \@spec ); | 
| 107 |  |  |  |  |  |  |  | 
| 108 | 18 |  |  |  |  | 278 | my @options = ( 'flag', 'ctid', @{ $vzctl{ $arg[0] } } ); | 
|  | 18 |  |  |  |  | 113 |  | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 18 | 50 |  |  |  | 232 | return wantarray ? @options : \@options; | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | #################################### | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | my @capabilities = qw( | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | chown dac_override dac_read_search fowner fsetid ipc_lock ipc_owner kill | 
| 120 |  |  |  |  |  |  | lease linux_immutable mknod net_admin net_bind_service net_broadcast | 
| 121 |  |  |  |  |  |  | net_raw setgid setpcap setuid setveid sys_admin sys_boot sys_chroot | 
| 122 |  |  |  |  |  |  | sys_module sys_nice sys_pacct sys_ptrace sys_rawio sys_resource sys_time | 
| 123 |  |  |  |  |  |  | sys_tty_config ve_admin | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | ); | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | push @vzctl_exports, 'capabilities'; | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 1 | 50 |  | 1 | 1 | 3658 | sub capabilities { return wantarray ? @capabilities : \@capabilities } | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | #################################### | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | my @iptables_modules = qw( | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | ip_conntrack ip_conntrack_ftp ip_conntrack_irc ip_nat_ftp ip_nat_irc | 
| 137 |  |  |  |  |  |  | iptable_filter iptable_mangle iptable_nat ipt_conntrack ipt_helper | 
| 138 |  |  |  |  |  |  | ipt_length ipt_limit ipt_LOG ipt_multiport ipt_owner ipt_recent | 
| 139 |  |  |  |  |  |  | ipt_REDIRECT ipt_REJECT ipt_state ipt_tcpmss ipt_TCPMSS ipt_tos ipt_TOS | 
| 140 |  |  |  |  |  |  | ipt_ttl xt_mac | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | ); | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | push @vzctl_exports, 'iptables_modules'; | 
| 145 |  |  |  |  |  |  |  | 
| 146 | 1 | 50 |  | 1 | 1 | 5064 | sub iptables_modules { return wantarray ? @iptables_modules : \@iptables_modules } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | #################################### | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | my @features = qw( sysfs nfs sit ipip ppp ipgre bridge nfsd ); | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | push @vzctl_exports, 'features'; | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 1 | 50 |  | 1 | 1 | 142926 | sub features { return wantarray ? @features : \@features } | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | #################################### | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | my %validate = do { | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | my $capability_names = join q{|}, @capabilities; | 
| 162 |  |  |  |  |  |  | my $iptables_names   = join q{|}, @iptables_modules; | 
| 163 |  |  |  |  |  |  | my $features_names   = join q{|}, @features; | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | my %hash = ( | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | # XXX: Annoying.  Need to submit a bug for this. | 
| 168 |  |  |  |  |  |  | ## no critic qw( Variables::ProhibitPunctuationVars ) | 
| 169 |  |  |  |  |  |  | avnumproc  => { type => SCALAR, regex     => qr{^\d+[gmkp]?(?::\d+[gmkp]?)?$}i }, | 
| 170 |  |  |  |  |  |  | bootorder  => { type => SCALAR, regex     => qr{^\d+$} }, | 
| 171 |  |  |  |  |  |  | capability => { type => SCALAR, regex     => qr{^(?:$capability_names):(?:on|off)$}i }, | 
| 172 |  |  |  |  |  |  | cpumask    => { type => SCALAR, regex     => qr{^\d+(?:[,-]\d+)*|all$}i }, | 
| 173 |  |  |  |  |  |  | ctid       => { type => SCALAR, callbacks => { 'validate ctid' => \&_validate_ctid } }, | 
| 174 |  |  |  |  |  |  | devices    => { type => SCALAR, regex     => qr{^(?:(?:[bc]:\d+:\d+)|all:(?:r?w?))|none$}i }, | 
| 175 |  |  |  |  |  |  | features   => { type => SCALAR, regex     => qr{^(?:$features_names):(?:on|off)$}i }, | 
| 176 |  |  |  |  |  |  | flag       => { type => SCALAR, regex     => qr{^quiet|verbose$}i }, | 
| 177 |  |  |  |  |  |  | force      => { type => UNDEF }, | 
| 178 |  |  |  |  |  |  | ioprio     => { type => SCALAR, regex => qr{^[0-7]$} }, | 
| 179 |  |  |  |  |  |  | onboot     => { type => SCALAR, regex => qr{^yes|no$}i }, | 
| 180 |  |  |  |  |  |  | setmode    => { type => SCALAR, regex => qr{^restart|ignore$}i }, | 
| 181 |  |  |  |  |  |  | userpasswd => { type => SCALAR, regex => qr{^(?:\w+):(?:\w+)$} }, | 
| 182 |  |  |  |  |  |  | ## use critic | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | applyconfig => { type => SCALAR, callbacks => { 'do not want empty strings' => sub { return $_[0] ne '' }, }, }, | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | command => { | 
| 187 |  |  |  |  |  |  | type      => SCALAR | ARRAYREF, | 
| 188 |  |  |  |  |  |  | callbacks => { | 
| 189 |  |  |  |  |  |  | 'do not want empty values' => sub { | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | return ref $_[0] eq '' | 
| 192 |  |  |  |  |  |  | ? do { $_[0] ne '' } | 
| 193 |  |  |  |  |  |  | : do { defined $_[0]->[0] && $_[0]->[0] ne '' }; | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | }, | 
| 196 |  |  |  |  |  |  | }, | 
| 197 |  |  |  |  |  |  | }, | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | ipadd => { | 
| 200 |  |  |  |  |  |  | type      => SCALAR | ARRAYREF, | 
| 201 |  |  |  |  |  |  | callbacks => { | 
| 202 |  |  |  |  |  |  | 'do these look like valid ip(s)?' => sub { | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | my @ips = ref $_[0] eq 'ARRAY' ? @{ $_[0] } : $_[0]; | 
| 205 |  |  |  |  |  |  | return unless @ips; | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | # I'd rather not do | 
| 208 | 20 |  |  | 20 |  | 25707 | no warnings 'uninitialized'; ## no critic qw( TestingAndDebugging::ProhibitNoWarnings ) | 
|  | 20 |  |  |  |  | 46 |  | 
|  | 20 |  |  |  |  | 4370 |  | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | # but | 
| 211 |  |  |  |  |  |  | # my @bad_ips = grep { defined    && ! /^$RE{net}{IPv4}$/ } @ips; | 
| 212 |  |  |  |  |  |  | # my @bad_ips = grep { defined $_ && ! /^$RE{net}{IPv4}$/ } @ips; | 
| 213 |  |  |  |  |  |  | # don't work and I'm not sure what else to try. | 
| 214 |  |  |  |  |  |  | my @bad_ips = grep { ! /^$RE{net}{IPv4}$/ } @ips; | 
| 215 |  |  |  |  |  |  | return ! @bad_ips;  # return 1 if there are no bad ips, undef otherwise. | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | #NOTE: I can't find a way to modify the incoming data, and it may not | 
| 218 |  |  |  |  |  |  | #      be a good idea to do that in any case. Unless, and until, I can | 
| 219 |  |  |  |  |  |  | #      figure out how to do this the right way this will be an atomic | 
| 220 |  |  |  |  |  |  | #      operation. It's either all good, or it's not. | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | }, | 
| 223 |  |  |  |  |  |  | }, | 
| 224 |  |  |  |  |  |  | }, | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | ipdel => { | 
| 227 |  |  |  |  |  |  | type      => SCALAR | ARRAYREF, | 
| 228 |  |  |  |  |  |  | callbacks => { | 
| 229 |  |  |  |  |  |  | 'do these look like valid ip(s)?' => sub { | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | my @ips = ref $_[0] eq 'ARRAY' ? @{ $_[0] } : $_[0]; | 
| 232 |  |  |  |  |  |  | return unless @ips; | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | # see notes for ipadd | 
| 235 | 20 |  |  | 20 |  | 116 | no warnings 'uninitialized'; ## no critic qw( TestingAndDebugging::ProhibitNoWarnings ) | 
|  | 20 |  |  |  |  | 40 |  | 
|  | 20 |  |  |  |  | 5077 |  | 
| 236 |  |  |  |  |  |  | my @bad_ips = grep { ! /^$RE{net}{IPv4}$/ } @ips; | 
| 237 |  |  |  |  |  |  | return 1 if any { $_ eq 'all' } @bad_ips; | 
| 238 |  |  |  |  |  |  | return ! @bad_ips; | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | #NOTE: See ipadd note. | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | }, | 
| 243 |  |  |  |  |  |  | }, | 
| 244 |  |  |  |  |  |  | }, | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | iptables => { | 
| 247 |  |  |  |  |  |  | type      => SCALAR | ARRAYREF, | 
| 248 |  |  |  |  |  |  | callbacks => { | 
| 249 |  |  |  |  |  |  | 'see manpage for list of valid iptables names' => sub { | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | my @names; | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | if ( ref $_[0] eq 'ARRAY' ) { | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | @names = @{ $_[0] }; | 
| 256 |  |  |  |  |  |  | return if @names == 0; | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | } else { | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | return if ! defined $_[0] || $_[0] eq ''; | 
| 261 |  |  |  |  |  |  | my $names = shift; | 
| 262 |  |  |  |  |  |  | @names = split /\s+/, $names; | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | # see notes for ipadd | 
| 267 | 20 |  |  | 20 |  | 117 | no warnings 'uninitialized'; ## no critic qw( TestingAndDebugging::ProhibitNoWarnings ) | 
|  | 20 |  |  |  |  | 45 |  | 
|  | 20 |  |  |  |  | 27494 |  | 
| 268 |  |  |  |  |  |  | my @bad_names = grep { ! /^(?:$iptables_names):o(?:n|ff)$/ } @names; | 
| 269 |  |  |  |  |  |  | return ! @bad_names; | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | #NOTE: See ipadd note. | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | }, | 
| 274 |  |  |  |  |  |  | }, | 
| 275 |  |  |  |  |  |  | }, | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | create_dumpfile => { | 
| 278 |  |  |  |  |  |  | type      => SCALAR, | 
| 279 |  |  |  |  |  |  | callbacks => { | 
| 280 |  |  |  |  |  |  | 'does it look like a valid filename?' => sub { | 
| 281 |  |  |  |  |  |  | return if $_[0] eq ''; | 
| 282 |  |  |  |  |  |  | my $file = sprintf 'file://localhost/%s', +shift; | 
| 283 |  |  |  |  |  |  | $file =~ /^$RE{URI}{file}$/; | 
| 284 |  |  |  |  |  |  | }, | 
| 285 |  |  |  |  |  |  | }, | 
| 286 |  |  |  |  |  |  | }, | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | restore_dumpfile => { type => SCALAR, callbacks => { 'does file exist?' => sub { -e ( +shift ) }, }, }, | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | devnodes => { | 
| 291 |  |  |  |  |  |  | type      => SCALAR, | 
| 292 |  |  |  |  |  |  | callbacks => { | 
| 293 |  |  |  |  |  |  | 'setting access to devnode' => sub { | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | return if ! defined $_[0] || $_[0] eq ''; | 
| 296 |  |  |  |  |  |  | return 1 if $_[0] eq 'none'; | 
| 297 |  |  |  |  |  |  | ( my $device = $_[0] ) =~ s/^(.*?):r?w?q?$/$1/; | 
| 298 |  |  |  |  |  |  | $device = "/dev/$device"; | 
| 299 |  |  |  |  |  |  | return -e $device; | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | }, | 
| 302 |  |  |  |  |  |  | }, | 
| 303 |  |  |  |  |  |  | }, | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | ); | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | my %same = ( | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | # SCALAR checks | 
| 310 |  |  |  |  |  |  | applyconfig => [ qw( | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | applyconfig_map config hostname name netif_add netif_del ostemplate | 
| 313 |  |  |  |  |  |  | pci_add pci_del private root searchdomain | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | ), | 
| 316 |  |  |  |  |  |  | ], | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | #XXX: Need to make 'config', 'ostemplate', 'private' and 'root' more | 
| 319 |  |  |  |  |  |  | #     robust.  We can pull the data from the global config file to help | 
| 320 |  |  |  |  |  |  | #     validate this info. | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | # SCALAR | ARRAYREF checks | 
| 323 |  |  |  |  |  |  | command => [qw( exec script )], | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | # UNDEF checks | 
| 326 |  |  |  |  |  |  | force => [qw( save wait )], | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | # INT checks | 
| 329 |  |  |  |  |  |  | bootorder => [qw( cpulimit cpus cpuunits quotatime quotaugidlimit )], | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | # yes or no checks | 
| 332 |  |  |  |  |  |  | onboot => [qw( disabled noatime )], | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | # ip checks | 
| 335 |  |  |  |  |  |  | ipadd => [qw( nameserver )], | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | # hard|soft limits | 
| 338 |  |  |  |  |  |  | avnumproc => [ qw( | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | dcachesize dgramrcvbuf diskinodes diskspace kmemsize lockedpages numfile | 
| 341 |  |  |  |  |  |  | numflock numiptent numothersock numproc numpty numsiginfo numtcpsock | 
| 342 |  |  |  |  |  |  | oomguarpages othersockbuf physpages privvmpages shmpages swappages | 
| 343 |  |  |  |  |  |  | tcprcvbuf tcpsndbuf vmguarpages | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | ), | 
| 346 |  |  |  |  |  |  | ], | 
| 347 |  |  |  |  |  |  | ); | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | for my $key ( keys %same ) { | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | $hash{ $_ } = $hash{ $key } for @{ $same{ $key } }; | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | %hash; | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | }; | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | ############################################################################ | 
| 360 |  |  |  |  |  |  | # Public functions | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | #XXX: Some of these should be extracted out into common module (OpenVZ.pm?) | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | my %global; | 
| 365 |  |  |  |  |  |  | my $spec = subcommand_specs( qw( flag ctid ) ); | 
| 366 |  |  |  |  |  |  | my $subcommands = join q{|}, sort( known_commands() ); | 
| 367 |  |  |  |  |  |  | $spec->{ subcommand } = { regex => qr/^$subcommands$/ }; ## no critic qw( ValuesAndExpressions::ProhibitAccessOfPrivateData ) | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | my %hash = ( command => 'vzctl' ); | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | push @vzctl_exports, 'vzctl'; | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | sub vzctl { ## no critic qw( Subroutines::RequireArgUnpacking ) | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 2478 | 100 |  | 2478 | 1 | 44684 | shift if blessed $_[0]; | 
| 376 |  |  |  |  |  |  |  | 
| 377 | 2478 |  |  |  |  | 114580 | my %arg = validate_with( params => @_, spec => $spec, allow_extra => 1, ); | 
| 378 |  |  |  |  |  |  |  | 
| 379 | 2466 |  |  |  |  | 76198 | my @params; | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | push @params, ( sprintf '--%s', delete $arg{ flag } ) | 
| 382 | 2466 | 100 |  |  |  | 31727 | if exists $arg{ flag }; | 
| 383 |  |  |  |  |  |  |  | 
| 384 | 2466 |  |  |  |  | 20662 | push @params, delete $arg{ subcommand }; | 
| 385 |  |  |  |  |  |  |  | 
| 386 | 2466 |  |  |  |  | 9188 | delete $arg{ ctid }; | 
| 387 | 2466 |  |  |  |  | 8376 | push @params, $global{ ctid }; | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 2466 |  |  |  |  | 12975 | for my $p ( keys %arg ) { | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | # XXX: Need better way to determine if this is a bare option | 
| 392 |  |  |  |  |  |  | #      maybe '!option' to indicate this option should be bare? | 
| 393 |  |  |  |  |  |  |  | 
| 394 | 2376 | 100 |  |  |  | 67033 | my $arg_name = $p =~ /^command|script$/ ? '' : "--$p"; | 
| 395 | 2376 |  |  |  |  | 20542 | my $ref = ref $arg{ $p }; | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 2376 | 100 |  |  |  | 23245 | if ( $ref eq 'ARRAY' ) { | 
|  |  | 50 |  |  |  |  |  | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 48 |  |  |  |  | 305 | push @params, ( $arg_name, $_ ) for @{ $arg{ $p } }; | 
|  | 48 |  |  |  |  | 791 |  | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | } elsif ( $ref eq '' ) { | 
| 402 |  |  |  |  |  |  |  | 
| 403 | 2328 |  |  |  |  | 6653 | push @params, $arg_name; | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | # coverage: I don't see a way to test for ! defined $arg{$p} | 
| 406 |  |  |  |  |  |  | # ... so we'll have to accept a 67% coverage for this one. | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 2328 | 100 | 66 |  |  | 47494 | push @params, $arg{ $p } | 
| 409 |  |  |  |  |  |  | if defined $arg{ $p } && $arg{ $p } ne ''; | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | } else { | 
| 412 |  |  |  |  |  |  |  | 
| 413 | 0 |  |  |  |  | 0 | croak "Don't know how to handle ref type $ref for $p"; | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  | } ## end for my $p ( keys %arg) | 
| 417 |  |  |  |  |  |  |  | 
| 418 | 2466 |  |  |  |  | 21063 | @params = grep { $_ ne '' } @params; | 
|  | 11400 |  |  |  |  | 45458 |  | 
| 419 |  |  |  |  |  |  |  | 
| 420 | 2466 |  |  |  |  | 10739 | $hash{ params } = \@params; | 
| 421 |  |  |  |  |  |  |  | 
| 422 | 2466 |  |  |  |  | 39661 | return execute( \%hash ); | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | } ## end sub vzctl | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | #################################### | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | push @vzctl_exports, 'subcommand_specs'; | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | sub subcommand_specs { ## no critic qw( Subroutines::RequireArgUnpacking ) | 
| 431 |  |  |  |  |  |  |  | 
| 432 | 74 | 50 |  | 74 | 1 | 231811 | shift if blessed $_[0]; | 
| 433 |  |  |  |  |  |  |  | 
| 434 | 74 |  |  |  |  | 2927 | my @args = validate_with( params => \@_, spec => [ { type => SCALAR } ], allow_extra => 1, ); | 
| 435 |  |  |  |  |  |  |  | 
| 436 | 74 |  |  |  |  | 471 | my %spec_hash; | 
| 437 |  |  |  |  |  |  |  | 
| 438 | 74 | 100 | 66 |  |  | 2506 | if ( defined $subcommands && $args[0] =~ /^$subcommands$/ ) { | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | # then build predefined specification hash | 
| 441 |  |  |  |  |  |  |  | 
| 442 | 54 |  |  |  |  | 117 | my @specs = @{ $vzctl{ +shift @args } }; | 
|  | 54 |  |  |  |  | 301 |  | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | # Every subcommand has these two at a minimum. | 
| 445 | 54 |  |  |  |  | 210 | unshift @specs, '[flag]', 'ctid'; | 
| 446 |  |  |  |  |  |  |  | 
| 447 | 54 |  |  |  |  | 151 | for my $spec ( @specs ) { | 
| 448 |  |  |  |  |  |  |  | 
| 449 | 318 |  |  |  |  | 1732 | my $optional = $spec =~ s/^\[(.*)\]$/$1/; | 
| 450 |  |  |  |  |  |  |  | 
| 451 | 318 | 50 |  |  |  | 928 | croak "Unknown spec $spec" | 
| 452 |  |  |  |  |  |  | unless exists $validate{ $spec }; | 
| 453 |  |  |  |  |  |  |  | 
| 454 | 318 | 50 |  | 0 |  | 1797 | next if any { /^-$spec$/ } @args; | 
|  | 0 |  |  |  |  | 0 |  | 
| 455 |  |  |  |  |  |  |  | 
| 456 | 318 |  |  |  |  | 1369 | $spec_hash{ $spec } = $validate{ $spec }; | 
| 457 |  |  |  |  |  |  |  | 
| 458 | 318 | 100 |  |  |  | 1050 | $spec_hash{ $spec }{ optional } = 1 | 
| 459 |  |  |  |  |  |  | if $optional; | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | } | 
| 462 |  |  |  |  |  |  | } ## end if ( defined $subcommands...) | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | # build custom specification hash if any args are left | 
| 465 |  |  |  |  |  |  |  | 
| 466 | 74 |  |  |  |  | 250 | for my $spec ( @args ) { | 
| 467 |  |  |  |  |  |  |  | 
| 468 | 40 | 50 |  |  |  | 135 | next if $spec =~ /^-/; | 
| 469 | 40 | 50 |  |  |  | 114 | next if exists $spec_hash{ $spec }; | 
| 470 |  |  |  |  |  |  |  | 
| 471 | 40 | 50 |  |  |  | 114 | croak "Unknown spec $spec" | 
| 472 |  |  |  |  |  |  | unless exists $validate{ $spec }; | 
| 473 |  |  |  |  |  |  |  | 
| 474 | 40 |  |  |  |  | 113 | $spec_hash{ $spec } = $validate{ $spec }; | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | } | 
| 477 |  |  |  |  |  |  |  | 
| 478 | 74 |  |  |  |  | 547 | return \%spec_hash; | 
| 479 |  |  |  |  |  |  |  | 
| 480 |  |  |  |  |  |  | } ## end sub subcommand_specs | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | ############################################################################ | 
| 483 |  |  |  |  |  |  | # Internal Functions | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | #XXX: Should be extracted out into common module (OpenVZ.pm?) | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | # Is the provided ctid a valid container identifier? | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | sub _validate_ctid { ## no critic qw( Subroutines::RequireArgUnpacking ) | 
| 490 |  |  |  |  |  |  |  | 
| 491 | 7894 | 50 |  | 7894 |  | 171302 | shift if blessed $_[0]; | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | #my ( $ctid, $params ) = @_; | 
| 494 | 7894 |  |  |  |  | 41094 | my $check_ctid = shift; | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | { | 
| 497 | 20 |  |  | 20 |  | 621 | no warnings qw( numeric uninitialized ); ## no critic qw( TestingAndDebugging::ProhibitNoWarnings ) | 
|  | 20 |  |  |  |  | 189 |  | 
|  | 20 |  |  |  |  | 11834 |  | 
|  | 7894 |  |  |  |  | 29192 |  | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | # coverage: we can't check against ! exists, so we'll have to live | 
| 500 |  |  |  |  |  |  | # with a 71% coverage on this one. | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | return 1 | 
| 503 |  |  |  |  |  |  | if ( exists $global{ ctid } && $global{ ctid } == $check_ctid ) | 
| 504 | 7894 | 100 | 100 |  |  | 462714 | || ( exists $global{ name } && $global{ name } eq $check_ctid ); | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 505 |  |  |  |  |  |  | } | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | # XXX: Need to modify this when vzlist is handled so we keep things | 
| 508 |  |  |  |  |  |  | # uncluttered. | 
| 509 |  |  |  |  |  |  |  | 
| 510 | 1317 |  |  |  |  | 25584 | my ( $stdout, $stderr, $syserr ) = execute( { command => 'vzlist', params => [ '-Ho', 'ctid,name', $check_ctid ], } ); | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | ## no critic qw( ErrorHandling::RequireUseOfExceptions ValuesAndExpressions::ProhibitMagicNumbers ) | 
| 513 | 1317 | 50 |  |  |  | 53339569 | croak 'vzlist did not execute' | 
| 514 |  |  |  |  |  |  | if $syserr == -1; | 
| 515 |  |  |  |  |  |  |  | 
| 516 | 1317 |  |  |  |  | 10197 | $syserr >>= 8; | 
| 517 |  |  |  |  |  |  |  | 
| 518 | 1317 | 100 |  |  |  | 133121 | croak "Invalid or unknown container ($check_ctid): $stderr" | 
| 519 |  |  |  |  |  |  | if $syserr == 1; | 
| 520 |  |  |  |  |  |  | ## use critic | 
| 521 |  |  |  |  |  |  |  | 
| 522 | 261 |  |  |  |  | 11360 | $stdout =~ s/^\s*(.*?)\s*$/$1/; | 
| 523 | 261 |  |  |  |  | 3278 | my ( $ctid, $name ) = split /\s+/, $stdout; | 
| 524 |  |  |  |  |  |  |  | 
| 525 | 261 |  |  |  |  | 2562 | $global{ ctid } = $ctid; | 
| 526 | 261 |  |  |  |  | 3668 | $global{ name } = $name; | 
| 527 |  |  |  |  |  |  |  | 
| 528 | 261 |  |  |  |  | 49757 | return 1; | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | } ## end sub _validate_ctid | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | # Generate the code for each of the subcommands | 
| 533 |  |  |  |  |  |  | # https://metacpan.org/module/Sub::Exporter#Export-Configuration | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | sub _generate_subcommand { ## no critic qw( Subroutines::RequireArgUnpacking ) | 
| 536 |  |  |  |  |  |  |  | 
| 537 | 36 | 50 |  | 36 |  | 5456 | shift if blessed $_[0]; | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | #XXX: Need to handle case of calling class using something like | 
| 540 |  |  |  |  |  |  | # | 
| 541 |  |  |  |  |  |  | # use OpenVZ::vzctl set => { -as => 'setip', arg => 'ipadd' }; | 
| 542 |  |  |  |  |  |  | # | 
| 543 |  |  |  |  |  |  | # and creating a sub that only accepts the ipadd parameter. | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | #my ( $class, $name, $arg, $collection ) = @_; | 
| 546 | 36 |  |  |  |  | 185 | my ( undef, $subcommand ) = @_; | 
| 547 | 36 |  |  |  |  | 520 | my $subcommand_spec = subcommand_specs( $subcommand ); | 
| 548 |  |  |  |  |  |  |  | 
| 549 | 36 |  |  |  |  | 70 | my %sub_spec; | 
| 550 |  |  |  |  |  |  |  | 
| 551 | 36 |  |  |  |  | 96 | $sub_spec{ spec } = $subcommand_spec; | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | return sub { | 
| 554 |  |  |  |  |  |  |  | 
| 555 | 7206 | 100 |  | 7206 |  | 57745732 | shift if blessed $_[0]; | 
| 556 |  |  |  |  |  |  |  | 
| 557 | 7206 |  |  |  |  | 49867 | $sub_spec{ params } = \@_; | 
| 558 |  |  |  |  |  |  |  | 
| 559 | 7206 |  |  |  |  | 475615 | my %arg = validate_with( %sub_spec ); | 
| 560 | 2466 |  |  |  |  | 239459 | $arg{ subcommand } = $subcommand; | 
| 561 | 2466 |  |  |  |  | 23197 | vzctl( \%arg ); | 
| 562 |  |  |  |  |  |  |  | 
| 563 | 36 |  |  |  |  | 373 | }; | 
| 564 |  |  |  |  |  |  | } ## end sub _generate_subcommand | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | # for oop stuff | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | # XXX: Do we need/want to support methods for the various options (what is returned from subcommand_specs)? | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | sub AUTOLOAD { ## no critic qw( Subroutines::RequireArgUnpacking ClassHierarchies::ProhibitAutoloading ) | 
| 571 |  |  |  |  |  |  |  | 
| 572 | 18 | 50 |  | 18 |  | 72228 | carp "$_[0] is not an object" | 
| 573 |  |  |  |  |  |  | unless blessed $_[0]; | 
| 574 |  |  |  |  |  |  |  | 
| 575 | 18 |  |  |  |  | 387 | ( my $subcommand = $AUTOLOAD ) =~ s/^.*:://; | 
| 576 |  |  |  |  |  |  |  | 
| 577 | 18 | 50 |  |  |  | 190 | carp "$subcommand is not a valid method" | 
| 578 |  |  |  |  |  |  | unless exists $vzctl{ $subcommand }; | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | ## no critic qw( TestingAndDebugging::ProhibitNoStrict References::ProhibitDoubleSigils ) | 
| 581 | 20 |  |  | 20 |  | 131 | no strict 'refs'; | 
|  | 20 |  |  |  |  | 42 |  | 
|  | 20 |  |  |  |  | 4182 |  | 
| 582 | 18 |  |  |  |  | 185 | *$AUTOLOAD = _generate_subcommand( undef, $subcommand ); | 
| 583 |  |  |  |  |  |  |  | 
| 584 | 18 |  |  |  |  | 194 | goto &$AUTOLOAD; | 
| 585 |  |  |  |  |  |  | ## use critic | 
| 586 |  |  |  |  |  |  |  | 
| 587 |  |  |  |  |  |  | } ## end sub AUTOLOAD | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  | # AUTOLOAD assumes DESTROY exists | 
| 590 | 0 |  |  | 0 |  |  | DESTROY { } | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | push @vzctl_exports, ( $_ => \&_generate_subcommand ) for keys %vzctl; | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | ############################################################################ | 
| 595 |  |  |  |  |  |  | # Setup exporter | 
| 596 |  |  |  |  |  |  |  | 
| 597 |  |  |  |  |  |  | my $config = { | 
| 598 |  |  |  |  |  |  |  | 
| 599 |  |  |  |  |  |  | exports    => \@vzctl_exports, | 
| 600 |  |  |  |  |  |  | groups     => {}, | 
| 601 |  |  |  |  |  |  | collectors => [], | 
| 602 |  |  |  |  |  |  |  | 
| 603 |  |  |  |  |  |  | }; | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | Sub::Exporter::setup_exporter( $config ); | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | }  # Ok, they're gone.  You can come out now.  Guys?  Hello? | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | 1; | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | __END__ |