]> git.proxmox.com Git - pve-manager.git/commitdiff
add openvz support (cli only for now)
authorDietmar Maurer <dietmar@proxmox.com>
Wed, 28 Sep 2011 12:08:54 +0000 (14:08 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Wed, 28 Sep 2011 12:08:54 +0000 (14:08 +0200)
PVE/API2/Makefile
PVE/API2/Nodes.pm
PVE/API2/OpenVZ.pm [new file with mode: 0644]
PVE/Makefile
PVE/OpenVZ.pm
bin/Makefile
bin/pvectl [changed mode: 0644->0755]

index 406576f76e5cff45045842aee9af8bbe0b454a0e..624cba1afeca1a06a15b1afa28d048e90ec731fd 100644 (file)
@@ -5,7 +5,8 @@ PERLSOURCE =                    \
        Nodes.pm                \
        Tasks.pm                \
        Network.pm              \
-       Services.pm
+       Services.pm             \
+       OpenVZ.pm
 
 all:
 
index 8b75f6d80ab2ed27618df818710b14bddd52c8e5..08a475c4e0cf26d1da6bbd209afd559751ac4301 100644 (file)
@@ -21,6 +21,7 @@ use PVE::API2::Tasks;
 use PVE::API2::Storage::Scan;
 use PVE::API2::Storage::Status;
 use PVE::API2::Qemu;
+use PVE::API2::OpenVZ;
 use JSON;
 
 use base qw(PVE::RESTHandler);
@@ -30,6 +31,11 @@ __PACKAGE__->register_method ({
     path => 'qemu',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::OpenVZ",  
+    path => 'openvz',
+});
+
 __PACKAGE__->register_method ({
     subclass => "PVE::API2::Services",  
     path => 'services',
@@ -92,6 +98,7 @@ __PACKAGE__->register_method ({
            { name => 'storage' },
            { name => 'upload' },
            { name => 'qemu' },
+           { name => 'openvz' },
            { name => 'network' },
            { name => 'network_changes' },
            ];
diff --git a/PVE/API2/OpenVZ.pm b/PVE/API2/OpenVZ.pm
new file mode 100644 (file)
index 0000000..1140fea
--- /dev/null
@@ -0,0 +1,204 @@
+package PVE::API2::OpenVZ;
+
+use strict;
+use warnings;
+use File::Basename;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_lock_file cfs_read_file);
+use PVE::Storage;
+use PVE::RESTHandler;
+use PVE::RPCEnvironment;
+use PVE::OpenVZ;
+use PVE::JSONSchema qw(get_standard_option);
+
+use base qw(PVE::RESTHandler);
+
+use Data::Dumper; # fixme: remove
+
+my $pve_base_ovz_config = <<__EOD;
+ONBOOT="no"
+
+PHYSPAGES="0:256M"
+SWAPPAGES="0:256M"
+KMEMSIZE="116M:128M"
+DCACHESIZE="58M:64M"
+LOCKEDPAGES="128M"
+PRIVVMPAGES="unlimited"
+SHMPAGES="unlimited"
+NUMPROC="unlimited"
+VMGUARPAGES="0:unlimited"
+OOMGUARPAGES="0:unlimited"
+NUMTCPSOCK="unlimited"
+NUMFLOCK="unlimited"
+NUMPTY="unlimited"
+NUMSIGINFO="unlimited"
+TCPSNDBUF="unlimited"
+TCPRCVBUF="unlimited"
+OTHERSOCKBUF="unlimited"
+DGRAMRCVBUF="unlimited"
+NUMOTHERSOCK="unlimited"
+NUMFILE="unlimited"
+NUMIPTENT="unlimited"
+
+# Disk quota parameters (in form of softlimit:hardlimit)
+DISKSPACE="unlimited:unlimited"
+DISKINODES="unlimited:unlimited"
+QUOTATIME="0"
+QUOTAUGIDLIMIT="0"
+
+# CPU fair scheduler parameter
+CPUUNITS="1000"
+CPUS="1"
+__EOD
+
+
+my $get_config_path = sub {
+    my $vmid = shift;
+    return "/etc/pve/openvz/${vmid}.conf";
+};
+
+__PACKAGE__->register_method({
+    name => 'vmlist', 
+    path => '', 
+    method => 'GET',
+    description => "OpenVZ container index (per node).",
+    proxyto => 'node',
+    protected => 1, # openvz proc files are only readable by root
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {},
+       },
+       links => [ { rel => 'child', href => "{vmid}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $vmstatus = PVE::OpenVZ::vmstatus();
+
+       return PVE::RESTHandler::hash_to_array($vmstatus, 'vmid');
+
+    }});
+
+__PACKAGE__->register_method({
+    name => 'create_vm', 
+    path => '', 
+    method => 'POST',
+    description => "Create new container.",
+    protected => 1,
+    proxyto => 'node',
+    parameters => {
+       additionalProperties => 0,
+       properties => PVE::OpenVZ::json_config_properties({
+           node => get_standard_option('pve-node'),
+           vmid => get_standard_option('pve-vmid'),
+           ostemplate => {
+               description => "The OS template.",
+               type => 'string', 
+               maxLength => 255,
+           },
+           password => { 
+               optional => 1, 
+               type => 'string',
+               description => "Sets root password inside container.",
+           },
+       }),
+    },
+    returns => { type => 'null'},
+    code => sub {
+       my ($param) = @_;
+
+       my $node = extract_param($param, 'node');
+
+       # fixme: fork worker?
+
+       my $vmid = extract_param($param, 'vmid');
+
+       my $password = extract_param($param, 'password');
+
+       my $stcfg = cfs_read_file("storage.cfg");
+
+       my $conf = PVE::OpenVZ::parse_ovz_config("/tmp/openvz/$vmid.conf", $pve_base_ovz_config);
+
+       my $code = sub {
+
+           my $basecfg_fn = &$get_config_path($vmid);
+
+           die "container $vmid already exists\n" if -f $basecfg_fn;
+
+           my $ostemplate = extract_param($param, 'ostemplate');
+
+           $ostemplate =~ s|^/var/lib/vz/template/cache/|local:vztmpl/|;
+
+           if ($ostemplate !~ m|^local:vztmpl/|) {
+               $ostemplate = "local:vztmpl/${ostemplate}";
+           }
+
+           my $tpath = PVE::Storage::path($stcfg, $ostemplate);
+           die "can't find OS template '$ostemplate'\n" if ! -f $tpath;
+
+           # hack: openvz does not support full paths
+           $tpath = basename($tpath);
+           $tpath =~ s/\.tar\.gz$//;
+
+           PVE::OpenVZ::update_ovz_config($conf, $param);
+
+           my $rawconf = PVE::OpenVZ::generate_raw_config($pve_base_ovz_config, $conf);
+
+           PVE::Tools::file_set_contents($basecfg_fn, $rawconf);
+
+           my $cmd = ['vzctl', '--skiplock', 'create', $vmid, '--ostemplate', $tpath ];
+
+           PVE::Tools::run_command($cmd);
+
+           # hack: vzctl '--userpasswd' starts the CT, but we want 
+           # to avoid that for create
+           PVE::OpenVZ::set_rootpasswd($vmid, $password) if defined($password);
+
+           return undef;
+       };
+
+       PVE::OpenVZ::lock_container($vmid, $code);
+    }});
+
+__PACKAGE__->register_method({
+    name => 'destroy_vm', 
+    path => '{vmid}', 
+    method => 'DELETE',
+    protected => 1,
+    proxyto => 'node',
+    description => "Destroy the container (also delete all uses files).",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           vmid => get_standard_option('pve-vmid'),
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+
+       my $user = $rpcenv->get_user();
+
+       my $vmid = $param->{vmid};
+
+       my $cmd = ['vzctl', 'destroy', $vmid ];
+
+       PVE::Tools::run_command($cmd);
+
+       return undef;
+    }});
+
+1;
index ccf4ca65e23d8f2a47464c813d5a585bd2a901d7..a5d3a37d80c9c6f569683fc11083301bec266a95 100644 (file)
@@ -7,6 +7,7 @@ PERLSOURCE =                    \
        API2Client.pm           \
        APIDaemon.pm            \
        REST.pm                 \
+       OpenVZ.pm               \
        APLInfo.pm
 
 all: pvecfg.pm ${SUBDIRS}
index 9046f6561dcf1eed2587f1cb7ce946a59862de65..1b8d95612aacedb62612ca8b31f92fc101804a73 100644 (file)
 package PVE::OpenVZ;
 
 use strict;
+use LockFile::Simple;
+use File::stat qw();
+use POSIX qw (LONG_MAX);
 use IO::Dir;
 use IO::File;
-use PVE::Config;
+use PVE::Tools qw(extract_param);
+use PVE::ProcFSTools;
+use PVE::Cluster qw(cfs_register_file cfs_read_file);
+use PVE::SafeSyslog;
+use PVE::INotify;
+use PVE::JSONSchema;
 
-my $confdir = "/etc/vz/conf";
+my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
+my $nodename = PVE::INotify::nodename();
 
-my $last_proc_vestat = {};
+sub config_list {
+    my $vmlist = PVE::Cluster::get_vmlist();
+    my $res = {};
+    return $res if !$vmlist || !$vmlist->{ids};
+    my $ids = $vmlist->{ids};
 
-$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
+    foreach my $vmid (keys %$ids) {
+       next if !$vmid; # skip VE0
+       my $d = $ids->{$vmid};
+       next if !$d->{node} || $d->{node} ne $nodename;
+       next if !$d->{type} || $d->{type} ne 'openvz';
+       $res->{$vmid}->{type} = 'openvz';
+    }
+    return $res;
+}
 
-my $kernel_version = `uname -r`;
+sub cfs_config_path {
+    my ($vmid, $node) = @_;
 
-sub vmlist {
+    $node = $nodename if !$node;
+    return "nodes/$node/openvz/$vmid.conf";
+}
 
-    my $res = {};
+my $last_proc_vestat = {};
 
-    my $fd = IO::Dir->new ($confdir) || 
-       die "unable to open dir '$confdir' - $!";
+sub vmstatus {
 
-    while (defined(my $de = $fd->read)) { 
-       if ($de =~ m/^(\d+)\.conf$/) {
-           my $veid = $1;
-           next if !$veid; # skip VE0
-           my $d = { 
-               status => 'stopped',
-               type => 'openvz',
-           };
+    my $list = config_list();
 
-           if (my $conf = PVE::Config::read_file ("$confdir/$de")) {
-               $d->{name} = $conf->{hostname}->{value} || "VM$veid";
-               $d->{name} =~ s/[\s]//g;
+    foreach my $vmid (keys %$list) {
+       my $d = $list->{$vmid};
+       $d->{status} = 'stopped';
 
-               $d->{cpus} = $conf->{cpus}->{value} || 1;
+       my $cfspath = cfs_config_path($vmid);
+       if (my $conf = PVE::Cluster::cfs_read_file($cfspath)) {
+           $d->{name} = $conf->{hostname}->{value} || "VM$vmid";
+           $d->{name} =~ s/[\s]//g;
 
-               $d->{disk} = 0;
-               $d->{maxdisk} = int ($conf->{diskspace}->{bar} / 1024);
+           $d->{cpus} = $conf->{cpus}->{value} || 1;
 
-               $d->{mem} = 0;
-               $d->{maxmem} = int (($conf->{vmguarpages}->{bar} * 4) / 1024);
-               $d->{nproc} = 0;
+           $d->{disk} = 0;
+           $d->{maxdisk} = int($conf->{diskspace}->{bar} * 1024);
 
-               $d->{uptime} = 0;
-               $d->{pctcpu} = 0;
-               $d->{relcpu} = 0;
+           $d->{mem} = 0;
+           $d->{maxmem} = int((($conf->{physpages}->{lim} + $conf->{swappages}->{lim})* 4096));
+           $d->{nproc} = 0;
 
-               if (my $ip = $conf->{ip_address}->{value}) {
-                   $ip =~ s/,;/ /g;
-                   $d->{ip} = (split(/\s+/, $ip))[0];
-               } else {
-                   $d->{ip} = '-';
-               }
-               $res->{"VEID_$veid"} = $d;
+           $d->{uptime} = 0;
+           $d->{pctcpu} = 0;
+           $d->{relcpu} = 0;
+
+           if (my $ip = $conf->{ip_address}->{value}) {
+               $ip =~ s/,;/ /g;
+               $d->{ip} = (split(/\s+/, $ip))[0];
+           } else {
+               $d->{ip} = '-';
            }
        }
     }
 
-    my $fh;
-
-    if ($fh = IO::File->new ("/proc/mounts", "r")) {
+    if (my $fh = IO::File->new ("/proc/mounts", "r")) {
        while (defined (my $line = <$fh>)) {
            if ($line =~ m|^/var/lib/vz/private/(\d+)\s+/var/lib/vz/root/|) {
-               $res->{"VEID_$1"}->{status} = 'mounted';
+               $list->{$1}->{status} = 'mounted' if defined($list->{$1});
            }
        }
+       close($fh);
     }
 
-    if ($fh = IO::File->new ("/proc/user_beancounters", "r")) {
-       my $veid;
+    if (my $fh = IO::File->new ("/proc/user_beancounters", "r")) {
+       my $vmid;
        while (defined (my $line = <$fh>)) {
            if ($line =~ m|\s*((\d+):\s*)?([a-z]+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$|) {
-               $veid = $2 if defined($2);
-               next if !$veid;
+               $vmid = $2 if defined($2);
+               next if !$vmid;
                my ($name, $held, $maxheld, $bar, $lim, $failcnt) = ($3, $4, $5, $6, $7, $8);
-               if (my $d = $res->{"VEID_$veid"}) {
-                   if ($name eq 'privvmpages') {
-                       $d->{mem} = int (($held *4) / 1024);
-                       $d->{maxmem} = int (($bar *4) / 1024);
+               if (my $d = $list->{$vmid}) {
+                   if ($name eq 'physpages') {
+                       $d->{mem} += int($held * 4096);
+                   } elsif ($name eq 'swappages') {
+                       $d->{mem} += int($held * 4096);
                    } elsif ($name eq 'numproc') {
                        $d->{nproc} = $held;
                    }
                }
            }
        }
+       close($fh);
     }
 
-    if ($fh = IO::File->new ("/proc/vz/vzquota", "r")) {
+    if (my $fh = IO::File->new ("/proc/vz/vzquota", "r")) {
        while (defined (my $line = <$fh>)) {
            if ($line =~ m|^(\d+):\s+/var/lib/vz/private/\d+$|) {
-               if (my $d = $res->{"VEID_$1"}) {
+               if (my $d = $list->{$1}) {
                    $line = <$fh>;
                    if ($line =~ m|^\s*1k-blocks\s+(\d+)\s+(\d+)\s|) {
-                       $d->{disk} = int ($1/1024);
-                       $d->{maxdisk} = int ($2/1024);
+                       $d->{disk} = int ($1 * 1024);
+                       $d->{maxdisk} = int ($2 * 1024);
                    }
                }
            }
        }
+       close($fh);
     }
 
-    my $cpuinfo = PVE::Utils::get_cpu_info();
     my $cpus = $cpuinfo->{cpus} || 1;
+    # Note: OpenVZ does not use POSIX::_SC_CLK_TCK
+    my $hz = 1000;
 
     # see http://wiki.openvz.org/Vestat
-    if ($fh = new IO::File ("/proc/vz/vestat", "r")) {
+    if (my $fh = new IO::File ("/proc/vz/vestat", "r")) {
        while (defined (my $line = <$fh>)) {
            if ($line =~ m/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/) {
-               my $veid = $1;
+               my $vmid = $1;
                my $user = $2;
                my $nice = $3;
                my $system = $4;
@@ -115,44 +137,888 @@ sub vmlist {
                my $sum = $8*$cpus; # uptime in jiffies * cpus = available jiffies
                my $used = $9; # used time in jiffies
 
-               # HZ is 250 in our kernel 2.6.24 kernel
-               # but HZ is 1000 in our kernel 2.6.18 kernel
-               my $hz = 250; 
-               $hz = 1000 if $kernel_version && $kernel_version =~ m/^2.6.18/;
-               my $uptime = int ($ut / $hz); # HZ is 250 in our kernel
+               my $uptime = int ($ut / $hz);
 
-               my $d = $res->{"VEID_$veid"};
+               my $d = $list->{$vmid};
                next if !$d;
 
                $d->{status} = 'running';
                $d->{uptime} = $uptime;
 
-               if (!defined ($last_proc_vestat->{$veid}) ||
-                   ($last_proc_vestat->{$veid}->{sum} > $sum)) {
-                   $last_proc_vestat->{$veid} = { used => 0, sum => 0, pctcpu => 0, relcpu => 0};
+               if (!defined ($last_proc_vestat->{$vmid}) ||
+                   ($last_proc_vestat->{$vmid}->{sum} > $sum)) {
+                   $last_proc_vestat->{$vmid} = { used => 0, sum => 0, pctcpu => 0, relcpu => 0};
                }
 
-               my $diff = $sum - $last_proc_vestat->{$veid}->{sum};
+               my $diff = $sum - $last_proc_vestat->{$vmid}->{sum};
 
                if ($diff > 1000) { # don't update too often
-                   my $useddiff = $used - $last_proc_vestat->{$veid}->{used};
+                   my $useddiff = $used - $last_proc_vestat->{$vmid}->{used};
                    my $pctcpu = int ($useddiff*100/$diff);
-                   $last_proc_vestat->{$veid}->{sum} = $sum;
-                   $last_proc_vestat->{$veid}->{used} = $used;
-                   $last_proc_vestat->{$veid}->{pctcpu} = $d->{pctcpu} = $pctcpu;
+                   $last_proc_vestat->{$vmid}->{sum} = $sum;
+                   $last_proc_vestat->{$vmid}->{used} = $used;
+                   $last_proc_vestat->{$vmid}->{pctcpu} = $d->{pctcpu} = $pctcpu;
 
-                   # fixme: openvz --cpus does not work currently
                    my $relcpu = $pctcpu;
-                   $last_proc_vestat->{$veid}->{relcpu} = $d->{relcpu} = $relcpu;
+                   $last_proc_vestat->{$vmid}->{relcpu} = $d->{relcpu} = $relcpu;
 
                } else {
-                   $d->{pctcpu} = $last_proc_vestat->{$veid}->{pctcpu};
-                   $d->{relcpu} = $last_proc_vestat->{$veid}->{relcpu};
+                   $d->{pctcpu} = $last_proc_vestat->{$vmid}->{pctcpu};
+                   $d->{relcpu} = $last_proc_vestat->{$vmid}->{relcpu};
                }
            }
        }
+       close($fh);
+    }
+
+    return $list;
+
+}
+
+my $confdesc = {
+    onboot => {
+       optional => 1,
+       type => 'boolean',
+       description => "Specifies whether a VM will be started during system bootup.",
+       default => 0,
+    },
+    cpus => {
+       optional => 1,
+       type => 'integer',
+       description => "The number of CPUs for this container.",
+       minimum => 1,
+       default => 1,
+    },
+    cpuunits => {
+       optional => 1,
+       type => 'integer',
+       description => "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0.",
+       minimum => 0,
+       maximum => 500000,
+       default => 1000,
+    },
+    memory => {
+       optional => 1,
+       type => 'integer',
+       description => "Amount of RAM for the VM in MB.",
+       minimum => 16,
+       default => 512,
+    },
+    swap => {
+       optional => 1,
+       type => 'integer',
+       description => "Amount of SWAP for the VM in MB.",
+       minimum => 16,
+       default => 512,
+    },
+    disk => {
+       optional => 1,
+       type => 'number',
+       description => "Amount of disk space for the VM in GB.",
+       minimum => 0.5,
+       default => 2,
+    },
+    quotatime => {
+       optional => 1,
+       type => 'integer',
+       description => "Set quota grace period (seconds).",
+       minimum => 0,
+       default => 0,
+    },
+    quotaugidlimit => {
+       optional => 1,
+       type => 'integer',
+       description => "Set maximum number of user/group IDs in a container for which disk quota inside the container will be accounted. If this value is set to 0, user and group quotas inside the container will not.",
+       minimum => 0,
+       default => 0,
+    },
+    hostname => {
+       optional => 1,
+       description => "Set a host name for the container.",
+       type => 'string',
+       maxLength => 255,
+    },
+    description => {
+       optional => 1,
+       type => 'string',
+       description => "Container description. Only used on the configuration web interface.",
+    },
+    searchdomain => {
+       optional => 1,
+       type => 'string',
+       description => "Sets DNS search domains for a container.",
+    },
+    nameserver => {
+       optional => 1,
+       type => 'string',
+       description => "Sets DNS server IP address for a container.",
+    },
+    ip_address => {
+       optional => 1,
+       type => 'string',
+       description => "Specifies the address the container will be assigned.",
+    },
+    netif => {
+       optional => 1,
+       type => 'string', format => 'pve-openvz-netif',
+       description => "Specifies network interfaces for the container.",
+    },
+};
+
+# add JSON properties for create and set function
+sub json_config_properties {
+    my $prop = shift;
+
+    foreach my $opt (keys %$confdesc) {
+       $prop->{$opt} = $confdesc->{$opt};
+    }
+
+    return $prop;
+}
+
+# read global vz.conf
+my $read_global_vz_config  = sub {
+
+    my $res = {
+       rootdir => '/var/lib/vz/root/$VEID', # note '$VEID' is a place holder
+       privatedir => '/var/lib/vz/private/$VEID', # note '$VEID' is a place holder
+       dumpdir => '/var/lib//vz/dump',
+       lockdir => '/var/lib/vz/lock',
+    };
+    
+    my $data = PVE::Tools::file_get_contents("/etc/vz/vz.conf");
+
+    if ($data =~ m/^\s*VE_PRIVATE=(.*)$/m) {
+       my $dir = $1;
+       $dir =~ s/^\"(.*)\"/$1/;
+       if ($dir !~ m/\$VEID/) {
+           warn "VE_PRIVATE does not contain '\$VEID' ('$dir')\n";
+       } else {
+           $res->{privatedir} = $dir;
+       }
+    }
+    if ($data =~ m/^\s*VE_ROOT=(.*)$/m) {
+       my $dir = $1;
+       $dir =~ s/^\"(.*)\"/$1/;
+       if ($dir !~ m/\$VEID/) {
+           warn "VE_ROOT does not contain '\$VEID' ('$dir')\n";
+       } else {
+           $res->{rootdir} = $dir;
+       }
+    }
+    if ($data =~ m/^\s*DUMPDIR=(.*)$/m) {
+       my $dir = $1;
+       $dir =~ s/^\"(.*)\"/$1/;
+       $dir =~ s|/\$VEID$||;
+       $res->{dumpdir} = $dir;
+    }
+    if ($data =~ m/^\s*LOCKDIR=(.*)$/m) {
+       my $dir = $1;
+       $dir =~ s/^\"(.*)\"/$1/;
+       $res->{lockdir} = $dir;
     }
 
     return $res;
+};
+
+my $global_vzconf =  &$read_global_vz_config();
+my $res_unlimited = LONG_MAX;
+
+sub parse_netif {
+    my ($data) = @_;
+
+    my $res = {};
+    return $res if !$data;
+
+    foreach my $iface (split (/;/, $data)) {
+       my $d = {};
+       foreach my $pv (split (/,/, $iface)) {
+           if ($pv =~ m/^(ifname|mac|bridge|host_ifname|host_mac)=(.+)$/) {
+               $d->{$1} = $2;
+           }
+       }
+       if ($d->{ifname}) {
+           $d->{raw} = $data;
+           $res->{$d->{ifname}} = $d;
+       } else {
+           return undef;
+       }
+    }
+
+    return $res;
+}
+
+PVE::JSONSchema::register_format('pve-openvz-netif', \&verify_netif);
+sub verify_netif {
+    my ($value, $noerr) = @_;
+
+    return $value if parse_netif($value);
+
+    return undef if $noerr;
+
+    die "unable to parse --netif value";
+}
+
+sub parse_res_num_ignore {
+    my ($key, $text) = @_;
+
+    if ($text =~ m/^(\d+|unlimited)(:.*)?$/) {
+       return { bar => $1 eq 'unlimited' ? $res_unlimited : $1 };
+    }
+
+    return undef;
+}
+
+sub parse_res_num_num {
+    my ($key, $text) = @_;
+
+    if ($text =~ m/^(\d+|unlimited)(:(\d+|unlimited))?$/) {
+       my $res = { bar => $1 eq 'unlimited' ? $res_unlimited : $1 };
+       if (defined($3)) {
+           $res->{lim} = $3 eq 'unlimited' ? $res_unlimited : $3;
+       } else {
+           $res->{lim} = $res->{bar};
+       }
+       return $res;
+    }
+
+    return undef;
+}
+
+sub parse_res_bar_limit {
+    my ($text, $base) = @_;
+
+    return $res_unlimited if $text eq 'unlimited';
+
+    if ($text =~ m/^(\d+)([TGMKP])?$/i) {
+       my $val = $1;
+       my $mult = lc($2);
+       if ($mult eq 'k') {
+           $val = $val * 1024;
+       } elsif ($mult eq 'm') {
+           $val = $val * 1024 * 1024;
+       } elsif ($mult eq 'g') {
+           $val = $val * 1024 * 1024 * 1024;
+       } elsif ($mult eq 't') {
+           $val = $val * 1024 * 1024 * 1024 * 1024;
+       } elsif ($mult eq 'p') {
+           $val = $val * 4096;
+       } else {
+           return $val;
+       }
+       return int($val/$base);
+    }
+
+    return undef;
+}
+
+sub parse_res_bytes_bytes {
+    my ($key, $text) = @_;
+
+    my @a = split(/:/, $text);
+    $a[1] = $a[0] if !defined($a[1]);
+    
+    my $bar = parse_res_bar_limit($a[0], 1);
+    my $lim = parse_res_bar_limit($a[1], 1);
+
+    if (defined($bar) && defined($lim)) {
+       return { bar => $bar, lim => $lim };
+    }
+
+    return undef;
+}
+
+sub parse_res_block_block {
+    my ($key, $text) = @_;
+
+    my @a = split(/:/, $text);
+    $a[1] = $a[0] if !defined($a[1]);
+    
+    my $bar = parse_res_bar_limit($a[0], 1024);
+    my $lim = parse_res_bar_limit($a[1], 1024);
+
+    if (defined($bar) && defined($lim)) {
+       return { bar => $bar, lim => $lim };
+    }
+
+    return undef;
+}
+
+sub parse_res_pages_pages {
+    my ($key, $text) = @_;
+
+    my @a = split(/:/, $text);
+    $a[1] = $a[0] if !defined($a[1]);
+    
+    my $bar = parse_res_bar_limit($a[0], 4096);
+    my $lim = parse_res_bar_limit($a[1], 4096);
+
+    if (defined($bar) && defined($lim)) {
+       return { bar => $bar, lim => $lim };
+    }
+
+    return undef;
+}
+
+sub parse_res_pages_unlimited {
+    my ($key, $text) = @_;
+
+    my @a = split(/:/, $text);
+    
+    my $bar = parse_res_bar_limit($a[0], 4096);
+    if (defined($bar)) {
+       return { bar => $bar, lim => $res_unlimited };
+    }
+
+    return undef;
+}
+
+sub parse_res_pages_ignore {
+    my ($key, $text) = @_;
+
+    my @a = split(/:/, $text);
+    
+    my $bar = parse_res_bar_limit($a[0], 4096);
+    if (defined($bar)) {
+       return { bar => $bar };
+    }
+
+    return undef;
+}
+
+sub parse_res_ignore_pages {
+    my ($key, $text) = @_;
+
+    my @a = split(/:/, $text);
+    $a[1] = $a[0] if !defined($a[1]);
+    
+    my $lim = parse_res_bar_limit($a[1] , 4096);
+    if (defined($lim)) {
+       return { bar => 0, lim => $lim };
+    }
+
+    return undef;
+}
+
+sub parse_boolean {
+    my ($key, $text) = @_;
+
+    return { value => "yes" } if $text =~ m/^(yes|true|on|1)$/i;
+    return { value => "no" } if $text =~ m/^(no|false|off|0)$/i;
+
+    return undef;
+};
+
+sub parse_integer {
+    my ($key, $text) = @_;
+
+    if ($text =~ m/^(\d+)$/) {
+       return { value => int($1) };
+    }
+
+    return undef;
+};
+
+my $ovz_ressources = {
+    numproc => \&parse_res_num_ignore,
+    numtcpsock => \&parse_res_num_ignore,
+    numothersock => \&parse_res_num_ignore,
+    numfile => \&parse_res_num_ignore,    
+    numflock => \&parse_res_num_num,
+    numpty => \&parse_res_num_ignore,
+    numsiginfo => \&parse_res_num_ignore,
+    numiptent => \&parse_res_num_ignore,
+
+    vmguarpages => \&parse_res_pages_unlimited,
+    oomguarpages => \&parse_res_pages_unlimited,
+    lockedpages => \&parse_res_pages_ignore,
+    privvmpages => \&parse_res_pages_pages,
+    shmpages => \&parse_res_pages_ignore,
+    physpages => \&parse_res_pages_pages,
+    swappages => \&parse_res_ignore_pages,
+
+    kmemsize => \&parse_res_bytes_bytes,
+    tcpsndbuf => \&parse_res_bytes_bytes,
+    tcprcvbuf => \&parse_res_bytes_bytes,
+    othersockbuf => \&parse_res_bytes_bytes,
+    dgramrcvbuf => \&parse_res_bytes_bytes,
+    dcachesize => \&parse_res_bytes_bytes,
+
+    diskquota => \&parse_boolean,
+    diskspace => \&parse_res_block_block,
+    diskinodes => \&parse_res_num_num,
+    quotatime => \&parse_integer,
+    quotaugidlimit => \&parse_integer,
+
+    cpuunits => \&parse_integer,
+    cpulimit => \&parse_integer,
+    cpus => \&parse_integer,
+    cpumask => 'string',
+    meminfo => 'string',
+    iptables => 'string',
+
+    ip_address => 'string',
+    netif => 'string',
+    hostname => 'string',
+    nameserver => 'string',
+    searchdomain => 'string',
+
+    name => 'string',
+    description => 'string',
+    onboot => \&parse_boolean,
+    initlog => \&parse_boolean,
+    bootorder => \&parse_integer,
+    ostemplate => 'string',
+    ve_root => 'string',
+    ve_private => 'string',
+    disabled => \&parse_boolean,
+    origin_sample => 'string',
+    noatime => \&parse_boolean,
+    capability => 'string',
+    devnodes => 'string',
+    devices => 'string',
+    pci => 'string',
+    features => 'string',
+    ioprio => \&parse_integer,
+
+};
+
+sub parse_ovz_config {
+    my ($filename, $raw) = @_;
+
+    return undef if !defined($raw);
+
+    my $data = {
+       digest => Digest::SHA1::sha1_hex($raw),
+    };
+
+    $filename =~ m|/openvz/(\d+)\.conf$|
+       || die "got strange filename '$filename'";
+
+    my $vmid = $1;
+
+    while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
+       my $line = $1;
+
+       next if $line =~ m/^\#/;
+       next if $line =~ m/^\s*$/;
+
+       if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) {
+           my $name = lc($1);
+           my $text = $2;
+
+           my $parser = $ovz_ressources->{$name};
+           if (!$parser || !ref($parser)) {
+               $data->{$name}->{value} = $text;
+               next;
+           } else {
+               if (my $res = &$parser($name, $text)) {
+                   $data->{$name} = $res;
+                   next;
+               }
+           }
+       }
+       die "unable to parse config line: $line\n";
+    }
+
+    return $data;
+}
+
+cfs_register_file('/openvz/', \&parse_ovz_config);
+
+sub format_res_value {
+    my ($key, $value) = @_;
+
+    return 'unlimited' if $value == $res_unlimited;
+
+    return 0 if $value == 0;
+
+    if ($key =~ m/pages$/) {
+        my $bytes = $value * 4096;
+       my $mb = int ($bytes / (1024 * 1024));
+       return "${mb}M" if $mb * 1024 * 1024 == $bytes;
+    } elsif ($key =~ m/space$/) {
+        my $bytes = $value * 1024;
+       my $gb = int ($bytes / (1024 * 1024 * 1024));
+       return "${gb}G" if $gb * 1024 * 1024 * 1024 == $bytes;
+       my $mb = int ($bytes / (1024 * 1024));
+       return "${mb}M" if $mb * 1024 * 1024 == $bytes;
+    } elsif ($key =~ m/size$/) {
+        my $bytes = $value;
+       my $mb = int ($bytes / (1024 * 1024));
+       return "${mb}M" if $mb * 1024 * 1024 == $bytes;
+    }
 
+    return $value;
 }
+
+sub format_res_bar_lim {
+    my ($key, $data) = @_;
+
+    if (defined($data->{lim}) && ($data->{lim} ne $data->{bar})) {
+       return format_res_value($key, $data->{bar}) . ":" . format_res_value($key, $data->{lim});     
+    } else {
+       return format_res_value($key, $data->{bar}); 
+    }
+}
+
+sub create_config_line {
+    my ($key, $data) = @_;
+
+    my $text;
+
+    if (defined($data->{value})) {
+       $text .= uc($key) . "=\"$data->{value}\"\n";
+    } elsif (defined($data->{bar})) {
+       my $tmp = format_res_bar_lim($key, $data);
+       $text .=  uc($key) . "=\"$tmp\"\n";     
+    }
+}
+
+sub update_ovz_config {
+    my ($veconf, $param) = @_;
+
+    my $changes = [];
+
+    # test if barrier or limit changed
+    my $push_bl_changes = sub {
+       my ($name, $bar, $lim) = @_;
+
+       my $old = format_res_bar_lim($name, $veconf->{$name});
+       my $new = format_res_bar_lim($name, { bar => $bar, lim => $lim });
+       if ($old ne $new) {
+           $veconf->{$name}->{bar} = $bar; 
+           $veconf->{$name}->{lim} = $lim;
+           push @$changes, "--$name", $new;
+       }
+    };
+
+    my $mem = int (($veconf->{physpages}->{lim} * 4) / 1024) || 512;
+    my $swap = int (($veconf->{swappages}->{lim} * 4) / 1024) || 0;
+
+    my $disk = ($veconf->{diskspace}->{bar} / (1024*1024)) || $res_unlimited;
+    my $cpuunits = $veconf->{cpuunits}->{value} || 1000;
+    my $quotatime = $veconf->{quotatime}->{value} || 0;
+    my $quotaugidlimit = $veconf->{quotaugidlimit}->{value} || 0;
+    my $cpus = $veconf->{cpus}->{value} || 1;
+
+    if ($param->{memory}) {
+       $mem = $param->{memory};
+    }
+
+    if (defined ($param->{swap})) {
+       $swap = $param->{swap};
+    }
+
+    if ($param->{disk}) {
+       $disk = $param->{disk};
+    }
+
+    if ($param->{cpuunits}) {
+       $cpuunits = $param->{cpuunits};
+    }
+
+    if (defined($param->{quotatime})) {
+       $quotatime = $param->{quotatime};
+    }
+
+    if (defined($param->{quotaugidlimit})) {
+       $quotaugidlimit = $param->{quotaugidlimit};
+    }
+
+    if ($param->{cpus}) {
+       $cpus = $param->{cpus};
+    }
+
+    # memory related parameter 
+
+    &$push_bl_changes('vmguarpages', 0, $res_unlimited);
+    &$push_bl_changes('oomguarpages', 0, $res_unlimited);
+    &$push_bl_changes('privvmpages', $res_unlimited, $res_unlimited);
+
+    # lock half of $mem
+    my $lockedpages = int($mem*1024/8);
+    &$push_bl_changes('lockedpages', $lockedpages, undef);
+
+    my $kmemsize = int($mem/2);
+    &$push_bl_changes('kmemsize', int($kmemsize/1.1)*1024*1024, $kmemsize*1024*1024);
+
+    my $dcachesize = int($mem/4);
+    &$push_bl_changes('dcachesize', int($dcachesize/1.1)*1024*1024, $dcachesize*1024*1024);
+
+    my $physpages = int($mem*1024/4);
+    &$push_bl_changes('physpages', 0, $physpages);
+
+    my $swappages = int($swap*1024/4);
+    &$push_bl_changes('swappages', 0, $swappages);
+
+
+    # disk quota parameters
+    if (($disk * 1.1) >= ($res_unlimited / (1024 * 1024))) {
+       &$push_bl_changes('diskspace', $res_unlimited, $res_unlimited);
+       &$push_bl_changes('diskinodes', $res_unlimited, $res_unlimited);
+    } else {
+       my $diskspace = int ($disk * 1024 * 1024);
+       my $diskspace_lim = int ($diskspace * 1.1);
+       &$push_bl_changes('diskspace', $diskspace, $diskspace_lim);
+       my $diskinodes = int ($disk * 200000);
+       my $diskinodes_lim = int ($disk * 220000);
+       &$push_bl_changes('diskinodes', $diskinodes, $diskinodes_lim);
+    }
+    if ($veconf->{'quotatime'}->{value} != $quotatime) {
+       $veconf->{'quotatime'}->{value} = $quotatime;
+       push @$changes, '--quotatime', "$quotatime";
+    }
+
+    if ($veconf->{'quotaugidlimit'}->{value} != $quotaugidlimit) {
+       $veconf->{'quotaugidlimit'}->{value} = $quotaugidlimit;
+       push @$changes, '--quotaugidlimit', "$quotaugidlimit";
+    }
+
+    # cpu settings
+
+    if ($veconf->{'cpuunits'}->{value} != $cpuunits) {
+       $veconf->{'cpuunits'}->{value} = $cpuunits;
+       push @$changes, '--cpuunits', "$cpuunits";
+    }
+
+    if ($veconf->{'cpus'}->{value} != $cpus) {
+       $veconf->{'cpus'}->{value} = $cpus;
+       push @$changes, '--cpus', "$cpus";
+    }
+
+    my $cond_set_boolean = sub {
+       my ($name) = @_;
+
+       return if !defined($param->{$name});
+
+       my $newvalue = $param->{$name} ? 'yes' : 'no';
+       my $oldvalue = $veconf->{$name}->{value};
+       if (!defined($oldvalue) || ($oldvalue ne $newvalue)) {
+           $veconf->{$name}->{value} = $newvalue;
+           push @$changes, "--$name", $newvalue;
+       }
+    };
+
+    my $cond_set_value = sub {
+       my ($name, $newvalue) = @_;
+
+       $newvalue = defined($newvalue) ? $newvalue : $param->{$name};
+       return if !defined($newvalue);
+
+       my $oldvalue = $veconf->{$name}->{value};
+       if (!defined($oldvalue) || ($oldvalue ne $newvalue)) {
+           $veconf->{$name}->{value} = $newvalue;
+           push @$changes, "--$name", $newvalue;
+       }
+    };
+
+    &$cond_set_boolean('onboot');
+    
+    &$cond_set_value('hostname');
+    &$cond_set_value('searchdomain');
+
+    if ($param->{'description'}) {
+       &$cond_set_value('description', PVE::Tools::encode_text($param->{'description'}));
+    }
+
+    if (defined($param->{ip_address})) {
+       my $iphash = {};
+       if (defined($veconf->{'ip_address'}) && $veconf->{'ip_address'}->{value}) {
+           foreach my $ip (split (/\s+/, $veconf->{ip_address}->{value})) {
+               $iphash->{$ip} = 1;
+           }
+       }
+       my $newhash = {};
+       foreach my $ip (PVE::Tools::split_list($param->{'ip_address'})) {
+           next if $ip !~ m|^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(/\d+)?$|;
+           $newhash->{$ip} = 1;
+           if (!$iphash->{$ip}) {
+               push @$changes, '--ipadd', $ip;
+               $iphash->{$ip} = 1; # only add once
+           }
+       }
+       foreach my $ip (keys %$iphash) {
+           if (!$newhash->{$ip}) {
+               push @$changes, '--ipdel', $ip;
+           }
+       }
+       $veconf->{'ip_address'}->{value} = join(' ', keys %$iphash);
+    }
+
+    if (defined($param->{netif})) {
+       my $ifaces = {};
+       if (defined ($veconf->{netif}) && $veconf->{netif}->{value}) {
+           $ifaces = parse_netif($veconf->{netif}->{value});
+       }
+       my $newif = parse_netif($param->{netif});
+
+       foreach my $ifname (sort keys %$ifaces) {
+           if (!$newif->{$ifname}) {
+               push @$changes, '--netif_del', $ifname;
+           }
+       }
+
+       my $newvalue = '';
+       foreach my $ifname (sort keys %$newif) {
+           $newvalue .= ';' if $newvalue;
+           $newvalue .= $ifname;
+           $newvalue .= $newif->{$ifname}->{mac} ? ",$newif->{$ifname}->{mac}" : ',';
+           $newvalue .= $newif->{$ifname}->{host_ifname} ? ",$newif->{$ifname}->{host_ifname}" : ',';
+           $newvalue .= $newif->{$ifname}->{host_mac} ? ",$newif->{$ifname}->{host_mac}" : ',';
+           $newvalue .= $newif->{$ifname}->{bridge} ? ",$newif->{$ifname}->{bridge}" : '';
+
+           if (!$ifaces->{$ifname} || ($ifaces->{$ifname}->{raw} ne $newif->{$ifname}->{raw})) {
+               push @$changes, '--netif_add', $newvalue;
+           }
+       }
+       $veconf->{netif}->{value} = $newvalue;
+    }
+
+    if (defined($param->{'nameserver'})) {
+       my $nshash = {};
+       foreach my $ns (PVE::Tools::split_list($param->{'nameserver'})) {
+           if (!$nshash->{$ns}) {
+               push @$changes, '--nameserver', $ns;
+               $nshash->{$ns} = 1;
+           }
+       }
+       $veconf->{'nameserver'}->{value} = join(' ', keys %$nshash);
+    }
+
+
+    foreach my $nv (@$changes) { print "CHANGE: $nv\n"; }
+
+    return $changes;
+}
+
+sub generate_raw_config {
+    my ($raw, $conf) = @_;
+
+    my $text = '';
+
+    my $found = {};
+
+    while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
+       my $line = $1;
+
+       if ($line =~ m/^\#/ || $line =~ m/^\s*$/) {
+           $text .= "$line\n";
+           next;
+       }
+
+       if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) {
+           my $name = lc($1);
+           if ($conf->{$name}) {
+               $found->{$name} = 1;
+               if (my $line = create_config_line($name, $conf->{$name})) {
+                   $text .= $line;
+               }
+           }
+       }
+    }
+
+    foreach my $key (keys %$conf) {
+       next if $found->{$key};
+       next if $key eq 'digest';
+       if (my $line = create_config_line($key, $conf->{$key})) {
+           $text .= $line;
+       }
+    }
+
+    return $text;
+}
+
+sub lock_container {
+    my ($vmid, $code, @param) = @_;
+
+    my $filename = $global_vzconf->{lockdir} . "/${vmid}.lck";
+    my $lock;
+    my $res;
+
+    eval {
+
+       my $lockmgr = LockFile::Simple->make(-format => '%f',
+                                            -autoclean => 1,
+                                            -max => 30, 
+                                            -delay => 2, 
+                                            -stale => 1,
+                                            -nfs => 0);
+
+       $lock = $lockmgr->lock($filename) || die "can't lock container $vmid\n";
+
+        $res = &$code(@param);
+
+    };
+    my $err = $@;
+
+    $lock->release() if $lock;
+
+    die $err if $err;
+
+    return $res;
+}
+
+sub replacepw {
+    my ($file, $epw) = @_;
+
+    my $tmpfile = "$file.$$";
+
+    eval  {
+       open (SRC, "<$file") ||
+           die "unable to open file '$file' - $!";
+
+       my $st = File::stat::stat(\*SRC) ||
+           die "unable to stat file - $!";
+
+       open (DST, ">$tmpfile") ||
+           die "unable to open file '$tmpfile' - $!";
+
+       # copy owner and permissions
+       chmod $st->mode, \*DST;
+       chown $st->uid, $st->gid, \*DST;
+       
+       while (defined (my $line = <SRC>)) {
+           $line =~ s/^root:[^:]*:/root:${epw}:/;
+           print DST $line;
+       }
+    };
+
+    my $err = $@;
+
+    close (SRC);
+    close (DST);
+
+    if ($err) {
+       unlink $tmpfile;
+    } else {
+       rename $tmpfile, $file;
+       unlink $tmpfile; # in case rename fails
+    }  
+}
+
+sub set_rootpasswd {
+    my ($vmid, $opt_rootpasswd) = @_;
+
+    my $vmdir = $global_vzconf->{privatedir};
+    $vmdir =~ s/\$VEID/$vmid/;
+
+    my $pwfile = "$vmdir/etc/passwd";
+
+    return if ! -f $pwfile;
+
+    my $shadow = "$vmdir/etc/shadow";
+
+    if (-f $shadow) {
+       replacepw ($shadow, $opt_rootpasswd);
+       replacepw ($pwfile, 'x');
+    } else {
+       replacepw ($pwfile, $opt_rootpasswd);
+    }
+}
+
index e8166a83fdc3fb034a98c96e3a369b6347543969..0f657d87b54e0d3395697b1e6a309f3c1ac4e46b 100644 (file)
@@ -13,6 +13,7 @@ SCRIPTS =                     \
        pveperf
 
 MANS =                                 \
+       pvectl.1                \
        pvestatd.1              \
        pvedaemon.1             \
        pveversion.1            \
@@ -20,8 +21,15 @@ MANS =                               \
 
 all: ${MANS}
 
-%.1: %
-       pod2man -n $* -s 1 -r "proxmox ${VERSION}" -c "Proxmox Documentation" <$* >$*.1
+%.1: %.1.pod
+       rm -f $@
+       cat $<|pod2man -n $* -s 1 -r ${VERSION} -c "Proxmox Documentation" >$@
+
+%.1.pod: %
+       podselect $*>$@
+
+pvectl.1.pod: pvectl
+       perl -I.. ./pvectl printmanpod >$@
 
 .PHONY: install 
 install: ${SCRIPTS} ${MANS}
@@ -38,5 +46,5 @@ distclean: clean
 
 .PHONY: clean
 clean:
-       rm -rf *~ ${MANS}
+       rm -rf *~ ${MANS} *.1.pod
        set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i $@; done
old mode 100644 (file)
new mode 100755 (executable)
index 2bfd423..32a8598
 #!/usr/bin/perl -w
 
 use strict;
-use Getopt::Long;
-use PVE::Config;
-use PVE::Utils;
-use PVE::Storage;
-use POSIX qw (LONG_MAX);
-use File::stat;
-use File::Basename;
 
-# fixme: log actions with syslog
-# fixme: lock ve
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_register_file cfs_read_file);
+use PVE::SafeSyslog;
+use PVE::INotify;
+use PVE::RPCEnvironment;
+use PVE::CLIHandler;
+use PVE::API2::OpenVZ;
 
-my $vzdir = "/etc/vz";
-my $vzconf = "$vzdir/vz.conf";
-my $confdir = "$vzdir/conf";
+use Data::Dumper; # fixme: remove
 
-my $global_vzconf =  read_glogal_vz_config ();
+use base qw(PVE::CLIHandler);
 
-# read global vz.conf
-sub read_glogal_vz_config {
-    local $/;
+$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
 
-    my $res = {
-       rootdir => '/var/lib/vz/root/$VEID',
-       privatedir => '/var/lib/vz/private/$VEID',
-    };
-        
-    return $res if ! -f $vzconf;
+initlog('pvectl');
 
-    open (TMP, "<$vzconf");
-    my $data = <TMP> || '';
-    close (TMP);
+die "please run as root\n" if $> != 0;
 
-    if ($data =~ m/^\s*VE_PRIVATE=\s*(.*\S)\s*$/m) {
-       my $dir = $1;
-       $dir =~ s/^\"(.*)\"/$1/;
-       $res->{privatedir} = $dir;
-    }
-    if ($data =~ m/^\s*VE_ROOT=\s*(.*\S)\s*$/m) {
-       my $dir = $1;
-       $dir =~ s/^\"(.*)\"/$1/;
-       $res->{rootdir} = $dir;
-    }
+PVE::INotify::inotify_init();
+my $nodename = PVE::INotify::nodename();
 
-    return $res;
-}
+my $rpcenv = PVE::RPCEnvironment->init('cli');
 
-sub get_private_dir {
-    my $veid = shift;
+$rpcenv->init_request();
+$rpcenv->set_language($ENV{LANG});
+$rpcenv->set_user('root@pam'); 
 
-    my $res = $global_vzconf->{privatedir};
+my $cmddef = {
 
-    $res =~ s/\$VEID/$veid/;
+    list => [ "PVE::API2::OpenVZ", 'vmlist', [],
+            { node => $nodename }, sub {
+                my $vmlist = shift;
 
-    return $res;
-}
+                exit 0 if (!scalar(@$vmlist));
 
-sub replacepw {
-    my ($file, $epw) = @_;
+                printf "%10s %-20s %-10s %-10s %12s %-10s\n", 
+                qw(VMID NAME STATUS MEM(MB)  DISK(GB));
 
-    my $tmpfile = "$file.$$";
+                foreach my $rec (sort { $a->{vmid} <=> $b->{vmid} } @$vmlist) {
+                    printf "%10s %-20s %-10s %-10s %-12.2f\n", $rec->{vmid}, $rec->{name} || '', 
+                    $rec->{status}, 
+                    ($rec->{maxmem} || 0)/(1024*1024), 
+                    ($rec->{maxdisk} || 0)/(1024*1024*1024);
+                }               
+             } ],
 
-    eval  {
-       open (SRC, "<$file") ||
-           die "unable to open file '$file' - $!";
+    create => [ 'PVE::API2::OpenVZ', 'create_vm', ['vmid', 'ostemplate'], { node => $nodename } ],
+    destroy => [ 'PVE::API2::OpenVZ', 'destroy_vm', ['vmid'], { node => $nodename } ],
 
-       my $st = stat (\*SRC) ||
-           die "unable to stat file - $!";
+};
 
-       open (DST, ">$tmpfile") ||
-           die "unable to open file '$tmpfile' - $!";
+my $cmd = shift;
 
-       # copy owner and permissions
-       chmod $st->mode, \*DST;
-       chown $st->uid, $st->gid, \*DST;
-       
-       while (defined (my $line = <SRC>)) {
-           $line =~ s/^root:[^:]*:/root:${epw}:/;
-           print DST $line;
-       }
-    };
+PVE::CLIHandler::handle_cmd($cmddef, "pvectl", $cmd, \@ARGV, undef, $0);
 
-    my $err = $@;
+exit 0;
 
-    close (SRC);
-    close (DST);
+__END__
 
-    if ($err) {
-       unlink $tmpfile;
-    } else {
-       rename $tmpfile, $file;
-       unlink $tmpfile; # in case rename fails
-    }  
-}
+=head1 NAME
 
-sub set_rootpasswd {
-    my ($vmdir, $opt_rootpasswd) = @_;
+pvectl - vzctl wrapper to manage OpenVZ containers
 
-    my $pwfile = "$vmdir/etc/passwd";
+=head1 SYNOPSIS
 
-    return if ! -f $pwfile;
+=include synopsis
 
-    my $shadow = "$vmdir/etc/shadow";
+=head1 DESCRIPTION
 
-    if (-f $shadow) {
-       replacepw ($shadow, $opt_rootpasswd);
-       replacepw ($pwfile, 'x');
-    } else {
-       replacepw ($pwfile, $opt_rootpasswd);
-    }
-}
+This is a small wrapper around vztl.
 
-sub print_usage {
-    my ($msg) = @_;
-
-    if ($msg) {
-       print STDERR "ERROR: $msg\n";
-    }
-    print STDERR "pvectl <command> <vmid> [parameters]\n";
-    print STDERR "pvectl [vzcreate|vzset] <vmid>     (openvz commands)\n";
-    print STDERR "        --ostemplate NAME          specify OS template\n";
-    print STDERR "        --mem  MBYTES              memory in MB (64 - 8192)\n";
-    print STDERR "        --swap  MBYTES             swap memory in MB (0 - 8192)\n";
-    print STDERR "        --disk GBYTE               disk space in GB (0.5 - 1024)\n";
-    print STDERR "        --cpus  N                  cpus (1 - 4)\n";
-    print STDERR "        --cpuunits  N              cpu units (8 - 500000)\n";
-    print STDERR "        --onboot [yes|no]          start at boot\n";
-    print STDERR "pvectl print <vmid>\n";
-}
-
-if (scalar (@ARGV) == 0) {
-    print_usage ();
-    exit (-1);
-}
-
-my $cmd = shift @ARGV;
-
-if (scalar (@ARGV) == 0) {
-    print_usage ();
-    exit (-1);
-}
-
-my $vmid = shift @ARGV;
-
-if ($vmid !~ m/^\d+$/) {
-    print_usage ("unable to parse <vmid>");
-    exit (-1);
-}
-
-# test if barrier or limit changed
-sub push_bl_changes {
-    my ($veconf, $changes, $name, $bar, $lim) = @_;
-
-    if (!defined ($veconf->{$name}->{bar}) || $veconf->{$name}->{bar} != $bar || 
-       !defined ($veconf->{$name}->{lim}) || $veconf->{$name}->{lim} != $lim) {
-       $veconf->{$name}->{bar} = $bar; 
-       $veconf->{$name}->{lim} = $lim;
-       push @$changes, "--$name", "$bar:$lim";
-    }
-
-}
-
-# we use lockedpages to store 'swap' settings - this is not really correct,
-# but its better than nothing.
-
-sub change_veconfig {
-    my ($veconf, $param) = @_;
-
-    my $changes = [];
-
-    my $mem = int (($veconf->{vmguarpages}->{bar} * 4) / 1024);
-    my $disk = $veconf->{diskspace}->{bar} / (1024*1024);
-    my $cpuunits = $veconf->{cpuunits}->{value} || 1000;
-    my $quotatime = $veconf->{quotatime}->{value} || 0;
-    my $quotaugidlimit = $veconf->{quotaugidlimit}->{value} || 0;
-    my $cpus = $veconf->{cpus}->{value} || 1;
-
-    my $swdiff = $veconf->{vmguarpages}->{bar} - $veconf->{lockedpages}->{bar};
-    my $swap = $swdiff > 0 ? int (($swdiff * 4) / 1024) : 0;
-    my $phymem = $mem - $swap;
-
-    if ($param->{mem}) {
-       $phymem = $param->{mem};
-    }
-    if (defined ($param->{swap})) {
-       $swap = $param->{swap};
-    }
-
-    if ($param->{disk}) {
-       $disk = $param->{disk};
-    }
-
-    if ($param->{cpuunits}) {
-       $cpuunits = $param->{cpuunits};
-    }
-
-    if (defined($param->{quotatime})) {
-       $quotatime = $param->{quotatime};
-    }
-
-    if (defined($param->{quotaugidlimit})) {
-       $quotaugidlimit = $param->{quotaugidlimit};
-    }
-
-    if ($param->{cpus}) {
-       $cpus = $param->{cpus};
-    }
-
-    $mem = $phymem + $swap;
-
-    # memory related parameter 
-
-    my $vmguarpages = int ($mem*1024/4);
-    push_bl_changes ($veconf, $changes, 'vmguarpages', $vmguarpages, LONG_MAX);
-    
-    my $privmax = int ($vmguarpages*1.1);
-    $privmax = $vmguarpages + 12500 if ($privmax - $vmguarpages) > 12500;
-
-    push_bl_changes ($veconf, $changes, 'oomguarpages', $vmguarpages, LONG_MAX);
-    push_bl_changes ($veconf, $changes, 'privvmpages', $vmguarpages, $privmax);
-
-    my $lockedpages;
-    if ($swap) {
-       $lockedpages = int (($mem - $swap)*1024/4);
-    } else {
-       $lockedpages = LONG_MAX;
-    }
-
-    push_bl_changes ($veconf, $changes, 'lockedpages', $lockedpages, $lockedpages);
-
-    # disk quota parameters
-
-    my $diskspace = int ($disk * 1024 * 1024);
-    my $diskspace_lim = int ($diskspace * 1.1);
-    push_bl_changes ($veconf, $changes, 'diskspace', $diskspace, $diskspace_lim);
-  
-    my $diskinodes = int ($disk * 200000);
-    my $diskinodes_lim = int ($disk * 220000);
-    push_bl_changes ($veconf, $changes, 'diskinodes', $diskinodes, $diskinodes_lim);
-
-    # cpu settings
-
-    if ($veconf->{'cpuunits'}->{value} != $cpuunits) {
-       push @$changes, '--cpuunits', "$cpuunits";
-    }
-
-    if ($veconf->{'quotatime'}->{value} != $quotatime) {
-       push @$changes, '--quotatime', "$quotatime";
-    }
-
-    if ($veconf->{'quotaugidlimit'}->{value} != $quotaugidlimit) {
-       push @$changes, '--quotaugidlimit', "$quotaugidlimit";
-    }
-
-    if ($veconf->{'cpus'}->{value} != $cpus) {
-       push @$changes, '--cpus', "$cpus";
-    }
-
-    #foreach my $nv (@$changes) {
-    #print "CHANGE: $nv\n";
-    #}
-
-    return $changes;
-}
-
-sub test_pve_config {
-    my ($veconf) = @_;
-
-    my $osample = $veconf->{origin_sample}->{value} || 'not set';
-    if ($osample ne 'pve.auto') {
-       print STDERR "VE $vmid is not managed by PVE (origin sample is $osample)\n";
-       exit (-1);
-    }
-}
-
-my $opt_mem;
-my $opt_swap;
-my $opt_disk;
-my $opt_cpus;
-my $opt_cpuunits;
-my $opt_quotatime;
-my $opt_quotaugidlimit;
-my $opt_ostemplate;
-my $opt_ipset;
-my $opt_hostname;
-my $opt_nameserver;
-my $opt_searchdomain;
-my $opt_onboot;
-my $opt_netif;
-my $opt_rootpasswd;
-my $opt_description;
-
-my $stcfg = PVE::Storage::load_config();
-
-if ($cmd eq 'vzcreate' || $cmd eq 'vzset') {
-
-    if (!GetOptions ('mem=i' => \$opt_mem,
-                    'swap=i' => \$opt_swap,
-                    'disk=f' => \$opt_disk,
-                    'cpus=i' => \$opt_cpus,
-                    'cpuunits=i' => \$opt_cpuunits,
-                    'quotatime=i' => \$opt_quotatime,
-                    'quotaugidlimit=i' => \$opt_quotaugidlimit,
-                    'ipset=s' => \$opt_ipset,
-                    'hostname=s' => \$opt_hostname,
-                    'description=s' => \$opt_description,
-                    'searchdomain=s' => \$opt_searchdomain,
-                    'nameserver=s@' => \$opt_nameserver,
-                    'onboot=s' => \$opt_onboot,
-                    'netif=s' => \$opt_netif,
-                    'rootpasswd=s' => \$opt_rootpasswd,
-                    'ostemplate=s' => \$opt_ostemplate)) {
-
-       print_usage ();
-       exit (-1);
-    }
-
-    PVE::Utils::check_vm_settings ({
-       mem => $opt_mem,
-       swap => $opt_swap,
-       disk => $opt_disk,
-       onboot => $opt_onboot,
-       cpuunits => $opt_cpuunits,
-       cpus => $opt_cpus });
-
-    if ($cmd eq 'vzcreate') {
-
-       if (!$opt_ostemplate) {
-           die "no --ostemplate specified\n";
-       }
-
-       if ($opt_ostemplate !~ m![/:]!) {
-           $opt_ostemplate = "local:vztmpl/${opt_ostemplate}";
-       }
-
-       my (undef, $volid) = PVE::Storage::path_to_volume_id ($stcfg, $opt_ostemplate);
-       die "can't find OS template '$opt_ostemplate'\n" if !$volid;
-
-       my $tpath = PVE::Storage::path ($stcfg, $volid);
-
-       die "can't find OS template '$opt_ostemplate'\n" if ! -f $tpath;
-
-       # hack: openvz does not support full paths
-       $tpath = basename ($tpath);
-       $tpath =~ s/\.tar\.gz$//;
-       
-       if (-f "$confdir/${vmid}.conf") {
-           print STDERR "VE $vmid already exists\n";
-           exit (-1);
-       }
-
-       my $cmd = ['vzctl', 'create', $vmid, '--ostemplate', $tpath,
-                  '--config', 'pve.auto'];
-       eval {
-           my $out = PVE::Utils::run_command ($cmd);
-       };
-
-       my $err = $@;
-
-       if ($err) {
-           print STDERR "unable to create VE $vmid: $err\n";
-           exit (-1);
-       }
-
-    } else {
-
-       if (defined ($opt_ostemplate)) {
-           print_usage ("unable to set --ostemplate");
-           exit (-1);
-       }
-
-       # test existence
-       if (! -f "$confdir/${vmid}.conf") {
-           print STDERR "VE $vmid does not exist\n";
-           exit (-1);
-       }
-
-       if (defined ($opt_rootpasswd)) {
-           print_usage ("option --rootpasswd not allowed");
-           exit (-1);
-       }
-    }
-
-    my $veconf = PVE::Config::get_veconfig ($vmid);
-
-    test_pve_config ($veconf);
-
-    my $changes = change_veconfig ($veconf, {
-       mem => $opt_mem, 
-       swap => $opt_swap, 
-       cpus => $opt_cpus, 
-       cpuunits => $opt_cpuunits, 
-       quotatime => $opt_quotatime, 
-       quotaugidlimit => $opt_quotaugidlimit, 
-       disk => $opt_disk});
-
-    if ($opt_hostname) {
-       push @$changes, '--hostname', $opt_hostname;
-    }
-
-    if (defined ($opt_description)) {
-       push @$changes, '--description', $opt_description;
-    }
-
-    if ($opt_searchdomain) {
-       push @$changes, '--searchdomain', $opt_searchdomain;
-    }
-
-    if (defined ($opt_ipset)) {
-       my $iphash = {};
-       if (defined ($veconf->{ip_address}) && 
-           $veconf->{ip_address}->{value}) {
-           foreach my $ip (split (/\s+/, $veconf->{ip_address}->{value})) {
-               $iphash->{$ip} = 1;
-           }
-       }
-       my $newhash = {};
-       foreach my $ip (split (/\s*[,;\s]\s*/, $opt_ipset)) {
-           next if $ip !~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
-           $newhash->{$ip} = 1;
-           if (!$iphash->{$ip}) {
-               push @$changes, '--ipadd', $ip;
-               $iphash->{$ip} = 1; # only add once
-           }
-       }
-       foreach my $ip (keys %$iphash) {
-           if (!$newhash->{$ip}) {
-               push @$changes, '--ipdel', $ip;
-           }
-       }
-    }
-
-    if (defined ($opt_netif)) {
-       my $ifaces = {};
-       if (defined ($veconf->{netif}) && 
-           $veconf->{netif}->{value}) {
-
-           $ifaces = PVE::Config::parse_netif ($veconf->{netif}->{value});
-       }
-       my $newif =  PVE::Config::parse_netif ($opt_netif);
-
-       foreach my $ifname (sort keys %$ifaces) {
-           if (!$newif->{$ifname}) {
-               push @$changes, '--netif_del', $ifname;
-           }
-       }
-
-       foreach my $ifname (sort keys %$newif) {
-           my $param = $ifname;
-           $param .= $newif->{$ifname}->{mac} ? ",$newif->{$ifname}->{mac}" : ',';
-           $param .= $newif->{$ifname}->{host_ifname} ? ",$newif->{$ifname}->{host_ifname}" : ',';
-           $param .= $newif->{$ifname}->{host_mac} ? ",$newif->{$ifname}->{host_mac}" : ',';
-           $param .= $newif->{$ifname}->{bridge} ? ",$newif->{$ifname}->{bridge}" : '';
-
-           if (!$ifaces->{$ifname} || ($ifaces->{$ifname}->{raw} ne $newif->{$ifname}->{raw})) {
-               push @$changes, '--netif_add', $param;
-           }
-       }
-    }
-
-    if ($opt_onboot) {
-       push @$changes, '--onboot', $opt_onboot;
-    }
-
-    foreach my $ns (@$opt_nameserver) {
-       push @$changes, '--nameserver', $ns;
-    }
-
-    if ($opt_rootpasswd) {
-       # hack: vzctl '--userpasswd' starts the CT, but we want to avoid that
-       # for create
-       if ($cmd eq 'vzcreate') {
-           my $vmdir = get_private_dir ($vmid);
-           set_rootpasswd ($vmdir, $opt_rootpasswd);
-       } else {
-           die "internal error"; # should not be reached
-       }
-    }
-
-    if (scalar (@$changes) <= 0) {
-       exit (0);
-    }
-       
-    my @cmd = ('vzctl', 'set', $vmid, @$changes, '--save');
-
-    my $cmdstr = join (' ', @cmd);
-    print "$cmdstr\n";
-
-    if (system (@cmd) != 0) {
-       print STDERR "unable to set parameters - command failed - $?\n";
-       exit (-1);
-    }
-
-} elsif ($cmd eq 'print') {
-
-    if (scalar (@ARGV) != 0) {
-       print_usage ();
-       exit (-1);
-    }
-
-    my $veconf = PVE::Config::get_veconfig ($vmid);
-
-    test_pve_config ($veconf);
-
-    print "Resource settings for VE $vmid:\n";
-    my $mem = int (($veconf->{vmguarpages}->{bar} * 4) / 1024);
-
-    my $swdiff = $veconf->{vmguarpages}->{bar} - $veconf->{lockedpages}->{bar};
-    my $swap = $swdiff > 0 ? int (($swdiff * 4) / 1024) : 0;
-    $mem = $mem - $swap;
-
-    print "Memory: $mem MB\n";
-
-    print "SWAP: $swap MB\n";
-
-    my $disk = $veconf->{diskspace}->{bar} / (1024*1024);
-
-    printf "Disk Space: %0.2f GB\n", $disk;
-
-    my $cpu = $veconf->{cpuunits}->{value};
-
-    print "CPU Units: $cpu\n";
-        
-} else {
-    print_usage ("no such command '$cmd'");
-    exit (-1);
-}
+=include pve_copyright