Commit 590d0595 authored by Dietmar Maurer's avatar Dietmar Maurer

add openvz support (cli only for now)

parent ca7916fa
......@@ -5,7 +5,8 @@ PERLSOURCE = \
Nodes.pm \
Tasks.pm \
Network.pm \
Services.pm
Services.pm \
OpenVZ.pm
all:
......
......@@ -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' },
];
......
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;
......@@ -7,6 +7,7 @@ PERLSOURCE = \
API2Client.pm \
APIDaemon.pm \
REST.pm \
OpenVZ.pm \
APLInfo.pm
all: pvecfg.pm ${SUBDIRS}
......
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};
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;
}
$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
sub cfs_config_path {
my ($vmid, $node) = @_;
my $kernel_version = `uname -r`;
$node = $nodename if !$node;
return "nodes/$node/openvz/$vmid.conf";
}
sub vmlist {
my $last_proc_vestat = {};
my $res = {};
sub vmstatus {
my $fd = IO::Dir->new ($confdir) ||
die "unable to open dir '$confdir' - $!";
my $list = config_list();
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',
};
foreach my $vmid (keys %$list) {
my $d = $list->{$vmid};
$d->{status} = 'stopped';
if (my $conf = PVE::Config::read_file ("$confdir/$de")) {
$d->{name} = $conf->{hostname}->{value} || "VM$veid";
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->{cpus} = $conf->{cpus}->{value} || 1;
$d->{disk} = 0;
$d->{maxdisk} = int ($conf->{diskspace}->{bar} / 1024);
$d->{maxdisk} = int($conf->{diskspace}->{bar} * 1024);
$d->{mem} = 0;
$d->{maxmem} = int (($conf->{vmguarpages}->{bar} * 4) / 1024);
$d->{maxmem} = int((($conf->{physpages}->{lim} + $conf->{swappages}->{lim})* 4096));
$d->{nproc} = 0;
$d->{uptime} = 0;
......@@ -52,62 +73,63 @@ sub vmlist {
} else {
$d->{ip} = '-';
}
$res->{"VEID_$veid"} = $d;
}
}
}
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->{$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 {
$d->{pctcpu} = $last_proc_vestat->{$veid}->{pctcpu};
$d->{relcpu} = $last_proc_vestat->{$veid}->{relcpu};
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);
}
}
......@@ -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
#!/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 {
$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
local $/;
initlog('pvectl');
my $res = {
rootdir => '/var/lib/vz/root/$VEID',
privatedir => '/var/lib/vz/private/$VEID',
};
die "please run as root\n" if $> != 0;
return $res if ! -f $vzconf;
PVE::INotify::inotify_init();
my $nodename = PVE::INotify::nodename();
open (TMP, "<$vzconf");
my $data = <TMP> || '';
close (TMP);
my $rpcenv = PVE::RPCEnvironment->init('cli');
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;
}
return $res;
}
sub get_private_dir {
my $veid = shift;
my $res = $global_vzconf->{privatedir};
$res =~ s/\$VEID/$veid/;
return $res;
}
sub replacepw {
my ($file, $epw) = @_;
my $tmpfile = "$file.$$";
eval {
open (SRC, "<$file") ||
die "unable to open file '$file' - $!";
my $st = 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 ($vmdir, $opt_rootpasswd) = @_;
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);
}
}
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";
}
$rpcenv->init_request();
$rpcenv->set_language($ENV{LANG});
$rpcenv->set_user('root@pam');
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 $cmddef = {
my $tpath = PVE::Storage::path ($stcfg, $volid);
list => [ "PVE::API2::OpenVZ", 'vmlist', [],
{ node => $nodename }, sub {
my $vmlist = shift;
die "can't find OS template '$opt_ostemplate'\n" if ! -f $tpath;
exit 0 if (!scalar(@$vmlist));
# hack: openvz does not support full paths
$tpath = basename ($tpath);
$tpath =~ s/\.tar\.gz$//;
printf "%10s %-20s %-10s %-10s %12s %-10s\n",
qw(VMID NAME STATUS MEM(MB) DISK(GB));
if (-f "$confdir/${vmid}.conf") {
print STDERR "VE $vmid already exists\n";
exit (-1);
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);
}
} ],
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;
}
create => [ 'PVE::API2::OpenVZ', 'create_vm', ['vmid', 'ostemplate'], { node => $nodename } ],
destroy => [ 'PVE::API2::OpenVZ', 'destroy_vm', ['vmid'], { node => $nodename } ],
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);
my $cmd = shift;
test_pve_config ($veconf);
PVE::CLIHandler::handle_cmd($cmddef, "pvectl", $cmd, \@ARGV, undef, $0);
print "Resource settings for VE $vmid:\n";
my $mem = int (($veconf->{vmguarpages}->{bar} * 4) / 1024);
exit 0;
my $swdiff = $veconf->{vmguarpages}->{bar} - $veconf->{lockedpages}->{bar};
my $swap = $swdiff > 0 ? int (($swdiff * 4) / 1024) : 0;
$mem = $mem - $swap;
__END__
print "Memory: $mem MB\n";
=head1 NAME
print "SWAP: $swap MB\n";
pvectl - vzctl wrapper to manage OpenVZ containers
my $disk = $veconf->{diskspace}->{bar} / (1024*1024);
=head1 SYNOPSIS
printf "Disk Space: %0.2f GB\n", $disk;
=include synopsis
my $cpu = $veconf->{cpuunits}->{value};
=head1 DESCRIPTION
print "CPU Units: $cpu\n";
This is a small wrapper around vztl.
} else {
print_usage ("no such command '$cmd'");
exit (-1);
}
=include pve_copyright
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment