From dafb62468e3656b6d0e7673017f48a02166948ac Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 27 Mar 2015 13:10:51 +0100 Subject: [PATCH] remove OpenVZ related code There is still no OpenVZ for kernel 3.10 or newer, so we remove that code now. --- PVE/API2/Makefile | 3 +- PVE/API2/Nodes.pm | 191 +---- PVE/API2/OpenVZ.pm | 1635 ------------------------------------------ PVE/Makefile | 2 - PVE/OpenVZ.pm | 1305 --------------------------------- PVE/OpenVZMigrate.pm | 335 --------- PVE/VZDump.pm | 20 +- PVE/VZDump/Makefile | 1 - PVE/VZDump/OpenVZ.pm | 312 -------- bin/Makefile | 6 - bin/pvectl | 106 --- bin/pvestatd | 52 -- bin/vzrestore | 102 --- 13 files changed, 7 insertions(+), 4063 deletions(-) delete mode 100644 PVE/API2/OpenVZ.pm delete mode 100644 PVE/OpenVZ.pm delete mode 100644 PVE/OpenVZMigrate.pm delete mode 100644 PVE/VZDump/OpenVZ.pm delete mode 100755 bin/pvectl delete mode 100755 bin/vzrestore diff --git a/PVE/API2/Makefile b/PVE/API2/Makefile index b16390197..a759d065a 100644 --- a/PVE/API2/Makefile +++ b/PVE/API2/Makefile @@ -12,8 +12,7 @@ PERLSOURCE = \ Pool.pm \ Tasks.pm \ Network.pm \ - Services.pm \ - OpenVZ.pm + Services.pm all: diff --git a/PVE/API2/Nodes.pm b/PVE/API2/Nodes.pm index 79c1b8b28..526e98ad5 100644 --- a/PVE/API2/Nodes.pm +++ b/PVE/API2/Nodes.pm @@ -20,7 +20,6 @@ use PVE::JSONSchema qw(get_standard_option); use PVE::AccessControl; use PVE::Storage; use PVE::Firewall; -use PVE::OpenVZ; use PVE::APLInfo; use PVE::HA::Config; use PVE::QemuServer; @@ -31,7 +30,6 @@ use PVE::API2::Tasks; use PVE::API2::Storage::Scan; use PVE::API2::Storage::Status; use PVE::API2::Qemu; -use PVE::API2::OpenVZ; use PVE::API2::VZDump; use PVE::API2::APT; use PVE::API2::Ceph; @@ -50,11 +48,6 @@ __PACKAGE__->register_method ({ path => 'ceph', }); -__PACKAGE__->register_method ({ - subclass => "PVE::API2::OpenVZ", - path => 'openvz', -}); - __PACKAGE__->register_method ({ subclass => "PVE::API2::VZDump", path => 'vzdump', @@ -142,7 +135,6 @@ __PACKAGE__->register_method ({ { name => 'scan' }, { name => 'storage' }, { name => 'qemu' }, - { name => 'openvz' }, { name => 'vzdump' }, { name => 'ubcfailcnt' }, { name => 'network' }, @@ -183,47 +175,6 @@ __PACKAGE__->register_method ({ return PVE::pvecfg::version_info(); }}); -__PACKAGE__->register_method({ - name => 'beancounters_failcnt', - path => 'ubcfailcnt', - permissions => { - check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]], - }, - method => 'GET', - proxyto => 'node', - proxyto => 'node', - protected => 1, # openvz /proc entries are only readable by root - description => "Get user_beancounters failcnt for all active containers.", - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - }, - }, - returns => { - type => 'array', - items => { - type => "object", - properties => { - id => { type => 'string' }, - failcnt => { type => 'number' }, - }, - }, - }, - code => sub { - my ($param) = @_; - - my $ubchash = PVE::OpenVZ::read_user_beancounters(); - - my $res = []; - foreach my $vmid (keys %$ubchash) { - next if !$vmid; - push @$res, { id => $vmid, failcnt => $ubchash->{$vmid}->{failcntsum} }; - - } - return $res; - }}); - __PACKAGE__->register_method({ name => 'status', path => 'status', @@ -1119,110 +1070,6 @@ __PACKAGE__->register_method({ return $res; }}); -__PACKAGE__->register_method({ - name => 'apl_download', - path => 'aplinfo', - method => 'POST', - permissions => { - check => ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']], - }, - description => "Download appliance templates.", - proxyto => 'node', - protected => 1, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - storage => get_standard_option('pve-storage-id'), - template => { type => 'string', maxLength => 255 }, - }, - }, - returns => { type => "string" }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $user = $rpcenv->get_user(); - - my $node = $param->{node}; - - my $list = PVE::APLInfo::load_data(); - - my $template = $param->{template}; - my $pd = $list->{all}->{$template}; - - raise_param_exc({ template => "no such template"}) if !$pd; - - my $cfg = cfs_read_file("storage.cfg"); - my $scfg = PVE::Storage::storage_check_enabled($cfg, $param->{storage}, $node); - - die "cannot download to storage type '$scfg->{type}'" - if !($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs'); - - die "unknown template type '$pd->{type}'\n" if $pd->{type} ne 'openvz'; - - die "storage '$param->{storage}' does not support templates\n" - if !$scfg->{content}->{vztmpl}; - - my $src = $pd->{location}; - my $tmpldir = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage}); - my $dest = "$tmpldir/$template"; - my $tmpdest = "$tmpldir/${template}.tmp.$$"; - - my $worker = sub { - my $upid = shift; - - print "starting template download from: $src\n"; - print "target file: $dest\n"; - - eval { - - if (-f $dest) { - my $md5 = (split (/\s/, `md5sum '$dest'`))[0]; - - if ($md5 && (lc($md5) eq lc($pd->{md5sum}))) { - print "file already exists $md5 - no need to download\n"; - return; - } - } - - local %ENV; - my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg'); - if ($dccfg->{http_proxy}) { - $ENV{http_proxy} = $dccfg->{http_proxy}; - } - - my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src); - if (system (@cmd) != 0) { - die "download failed - $!\n"; - } - - my $md5 = (split (/\s/, `md5sum '$tmpdest'`))[0]; - - if (!$md5 || (lc($md5) ne lc($pd->{md5sum}))) { - die "wrong checksum: $md5 != $pd->{md5sum}\n"; - } - - if (system ('mv', $tmpdest, $dest) != 0) { - die "unable to save file - $!\n"; - } - }; - my $err = $@; - - unlink $tmpdest; - - if ($err) { - print "\n"; - die $err if $err; - } - - print "download finished\n"; - }; - - return $rpcenv->fork_worker('download', undef, $user, $worker); - }}); - my $get_start_stop_list = sub { my ($nodename, $autostart) = @_; @@ -1239,16 +1086,7 @@ my $get_start_stop_list = sub { my $bootorder = LONG_MAX; - if ($d->{type} eq 'openvz') { - my $conf = PVE::OpenVZ::load_config($vmid); - return if $autostart && !($conf->{onboot} && $conf->{onboot}->{value}); - - if ($conf->{bootorder} && defined($conf->{bootorder}->{value})) { - $bootorder = $conf->{bootorder}->{value}; - } - $startup = { order => $bootorder }; - - } elsif ($d->{type} eq 'qemu') { + if ($d->{type} eq 'qemu') { my $conf = PVE::QemuServer::load_config($vmid); return if $autostart && !$conf->{onboot}; @@ -1331,11 +1169,7 @@ __PACKAGE__->register_method ({ my $default_delay = 0; my $upid; - if ($d->{type} eq 'openvz') { - return if PVE::OpenVZ::check_running($vmid); - print STDERR "Starting CT $vmid\n"; - $upid = PVE::API2::OpenVZ->vm_start({node => $nodename, vmid => $vmid }); - } elsif ($d->{type} eq 'qemu') { + if ($d->{type} eq 'qemu') { $default_delay = 3; # to redruce load return if PVE::QemuServer::check_running($vmid, 1); print STDERR "Starting VM $vmid\n"; @@ -1360,11 +1194,7 @@ __PACKAGE__->register_method ({ } } } else { - if ($d->{type} eq 'openvz') { - print STDERR "Starting CT $vmid failed: $status\n"; - } elsif ($d->{type} eq 'qemu') { - print STDERR "Starting VM $vmid failed: status\n"; - } + print STDERR "Starting VM $vmid failed: status\n"; } }; warn $@ if $@; @@ -1380,13 +1210,7 @@ my $create_stop_worker = sub { my ($nodename, $type, $vmid, $down_timeout) = @_; my $upid; - if ($type eq 'openvz') { - return if !PVE::OpenVZ::check_running($vmid); - my $timeout = defined($down_timeout) ? int($down_timeout) : 60; - print STDERR "Stopping CT $vmid (timeout = $timeout seconds)\n"; - $upid = PVE::API2::OpenVZ->vm_shutdown({node => $nodename, vmid => $vmid, - timeout => $timeout, forceStop => 1 }); - } elsif ($type eq 'qemu') { + if ($type eq 'qemu') { return if !PVE::QemuServer::check_running($vmid, 1); my $timeout = defined($down_timeout) ? int($down_timeout) : 60*3; print STDERR "Stopping VM $vmid (timeout = $timeout seconds)\n"; @@ -1475,12 +1299,7 @@ my $create_migrate_worker = sub { my ($nodename, $type, $vmid, $target) = @_; my $upid; - if ($type eq 'openvz') { - my $online = PVE::OpenVZ::check_running($vmid) ? 1 : 0; - print STDERR "Migrating CT $vmid\n"; - $upid = PVE::API2::OpenVZ->migrate_vm({node => $nodename, vmid => $vmid, target => $target, - online => $online }); - } elsif ($type eq 'qemu') { + if ($type eq 'qemu') { my $online = PVE::QemuServer::check_running($vmid, 1) ? 1 : 0; print STDERR "Migrating VM $vmid\n"; $upid = PVE::API2::Qemu->migrate_vm({node => $nodename, vmid => $vmid, target => $target, diff --git a/PVE/API2/OpenVZ.pm b/PVE/API2/OpenVZ.pm deleted file mode 100644 index a92c79fcf..000000000 --- a/PVE/API2/OpenVZ.pm +++ /dev/null @@ -1,1635 +0,0 @@ -package PVE::API2::OpenVZ; - -use strict; -use warnings; -use File::Basename; -use File::Path; -use POSIX qw (LONG_MAX); - -use PVE::SafeSyslog; -use PVE::Tools qw(extract_param run_command); -use PVE::Exception qw(raise raise_param_exc); -use PVE::INotify; -use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file); -use PVE::AccessControl; -use PVE::Storage; -use PVE::RESTHandler; -use PVE::RPCEnvironment; -use PVE::OpenVZ; -use PVE::OpenVZMigrate; -use PVE::JSONSchema qw(get_standard_option); -use PVE::API2::Firewall::VM; - -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_container_storage = sub { - my ($stcfg, $vmid, $veconf) = @_; - - my $path = PVE::OpenVZ::get_privatedir($veconf, $vmid); - my ($vtype, $volid) = PVE::Storage::path_to_volume_id($stcfg, $path); - my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1) if $volid; - return wantarray ? ($sid, $volname, $path) : $sid; -}; - -my $check_ct_modify_config_perm = sub { - my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_; - - return 1 if $authuser ne 'root@pam'; - - foreach my $opt (@$key_list) { - - if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') { - $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']); - } elsif ($opt eq 'disk' || $opt eq 'quotatime' || $opt eq 'quotaugidlimit') { - $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']); - } elsif ($opt eq 'memory' || $opt eq 'swap') { - $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']); - } elsif ($opt eq 'netif' || $opt eq 'ip_address' || $opt eq 'nameserver' || - $opt eq 'searchdomain' || $opt eq 'hostname') { - $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']); - } else { - $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']); - } - } - - return 1; -}; - -__PACKAGE__->register_method({ - name => 'vmlist', - path => '', - method => 'GET', - description => "OpenVZ container index (per node).", - permissions => { - description => "Only list VMs where you have VM.Audit permissons on /vms/.", - user => 'all', - }, - 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 $rpcenv = PVE::RPCEnvironment::get(); - my $authuser = $rpcenv->get_user(); - - my $vmstatus = PVE::OpenVZ::vmstatus(); - - my $res = []; - foreach my $vmid (keys %$vmstatus) { - next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1); - - my $data = $vmstatus->{$vmid}; - $data->{vmid} = $vmid; - push @$res, $data; - } - - return $res; - - }}); - -my $restore_openvz = sub { - my ($private, $archive, $vmid, $force) = @_; - - my $vzconf = PVE::OpenVZ::read_global_vz_config(); - my $conffile = PVE::OpenVZ::config_file($vmid); - my $cfgdir = dirname($conffile); - - my $root = $vzconf->{rootdir}; - $root =~ s/\$VEID/$vmid/; - - print "you choose to force overwriting VPS config file, private and root directories.\n" if $force; - - die "unable to create CT $vmid - container already exists\n" - if !$force && -f $conffile; - - die "unable to create CT $vmid - directory '$private' already exists\n" - if !$force && -d $private; - - die "unable to create CT $vmid - directory '$root' already exists\n" - if !$force && -d $root; - - my $conf; - - eval { - if ($force && -f $conffile) { - my $conf = PVE::OpenVZ::load_config($vmid); - - my $oldprivate = PVE::OpenVZ::get_privatedir($conf, $vmid); - rmtree $oldprivate if -d $oldprivate; - - my $oldroot = $conf->{ve_root} ? $conf->{ve_root}->{value} : $root; - rmtree $oldroot if -d $oldroot; - }; - - mkpath $private || die "unable to create private dir '$private'"; - mkpath $root || die "unable to create root dir '$root'"; - - my $cmd = ['tar', 'xpf', $archive, '--totals', '--sparse', '-C', $private]; - - if ($archive eq '-') { - print "extracting archive from STDIN\n"; - run_command($cmd, input => "<&STDIN"); - } else { - print "extracting archive '$archive'\n"; - run_command($cmd); - } - - my $backup_cfg = "$private/etc/vzdump/vps.conf"; - if (-f $backup_cfg) { - print "restore configuration to '$conffile'\n"; - - my $conf = PVE::Tools::file_get_contents($backup_cfg); - - $conf =~ s/VE_ROOT=.*/VE_ROOT=\"$root\"/; - $conf =~ s/VE_PRIVATE=.*/VE_PRIVATE=\"$private\"/; - $conf =~ s/host_ifname=veth[0-9]+\./host_ifname=veth${vmid}\./g; - - PVE::Tools::file_set_contents($conffile, $conf); - - foreach my $s (PVE::OpenVZ::SCRIPT_EXT) { - my $tfn = "$cfgdir/${vmid}.$s"; - my $sfn = "$private/etc/vzdump/vps.$s"; - if (-f $sfn) { - my $sc = PVE::Tools::file_get_contents($sfn); - PVE::Tools::file_set_contents($tfn, $sc); - } - } - } - - rmtree "$private/etc/vzdump"; - }; - - my $err = $@; - - if ($err) { - rmtree $private; - rmtree $root; - unlink $conffile; - foreach my $s (PVE::OpenVZ::SCRIPT_EXT) { - unlink "$cfgdir/${vmid}.$s"; - } - die $err; - } - - return $conf; -}; - -# create_vm is also used by vzrestore -__PACKAGE__->register_method({ - name => 'create_vm', - path => '', - method => 'POST', - description => "Create or restore a container.", - permissions => { - user => 'all', # check inside - description => "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " . - "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " . - "You also need 'Datastore.AllocateSpace' permissions on the storage.", - }, - 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 or backup file.", - type => 'string', - maxLength => 255, - }, - password => { - optional => 1, - type => 'string', - description => "Sets root password inside container.", - }, - storage => get_standard_option('pve-storage-id', { - description => "Target storage.", - default => 'local', - optional => 1, - }), - force => { - optional => 1, - type => 'boolean', - description => "Allow to overwrite existing container.", - }, - restore => { - optional => 1, - type => 'boolean', - description => "Mark this as restore task.", - }, - pool => { - optional => 1, - type => 'string', format => 'pve-poolid', - description => "Add the VM to the specified pool.", - }, - }), - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - my $password = extract_param($param, 'password'); - - my $storage = extract_param($param, 'storage') || 'local'; - - my $pool = extract_param($param, 'pool'); - - my $storage_cfg = cfs_read_file("storage.cfg"); - - my $scfg = PVE::Storage::storage_check_node($storage_cfg, $storage, $node); - - raise_param_exc({ storage => "storage '$storage' does not support openvz root directories"}) - if !$scfg->{content}->{rootdir}; - - my $private = PVE::Storage::get_private_dir($storage_cfg, $storage, $vmid); - - my $basecfg_fn = PVE::OpenVZ::config_file($vmid); - - if (defined($pool)) { - $rpcenv->check_pool_exist($pool); - $rpcenv->check_perm_modify($authuser, "/pool/$pool"); - } - - $rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace']); - - if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) { - # OK - } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) { - # OK - } elsif ($param->{restore} && $param->{force} && (-f $basecfg_fn) && - $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) { - # OK: user has VM.Backup permissions, and want to restore an existing VM - } else { - raise_perm_exc(); - } - - &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]); - - PVE::Storage::activate_storage($storage_cfg, $storage); - - my $conf = PVE::OpenVZ::parse_ovz_config("/tmp/openvz/$vmid.conf", $pve_base_ovz_config); - - my $ostemplate = extract_param($param, 'ostemplate'); - - my $archive; - - if ($ostemplate eq '-') { - die "pipe requires cli environment\n" - if $rpcenv->{type} ne 'cli'; - die "pipe can only be used with restore tasks\n" - if !$param->{restore}; - $archive = '-'; - } else { - $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate); - $archive = PVE::Storage::abs_filesystem_path($storage_cfg, $ostemplate); - } - - if (!defined($param->{searchdomain}) && - !defined($param->{nameserver})) { - - my $resolv = PVE::INotify::read_file('resolvconf'); - - $param->{searchdomain} = $resolv->{search} if $resolv->{search}; - - my @ns = (); - push @ns, $resolv->{dns1} if $resolv->{dns1}; - push @ns, $resolv->{dns2} if $resolv->{dns2}; - push @ns, $resolv->{dns3} if $resolv->{dns3}; - - $param->{nameserver} = join(' ', @ns) if scalar(@ns); - } - - # try to append domain to hostmane - if ($param->{hostname} && $param->{hostname} !~ m/\./ && - $param->{searchdomain}) { - - $param->{hostname} .= ".$param->{searchdomain}"; - } - - my $check_vmid_usage = sub { - if ($param->{force}) { - die "cant overwrite mounted container\n" - if PVE::OpenVZ::check_mounted($conf, $vmid); - } else { - die "CT $vmid already exists\n" if -f $basecfg_fn; - } - }; - - my $code = sub { - - &$check_vmid_usage(); # final check after locking - - PVE::OpenVZ::update_ovz_config($vmid, $conf, $param); - - my $rawconf = PVE::OpenVZ::generate_raw_config($pve_base_ovz_config, $conf); - - PVE::Cluster::check_cfs_quorum(); - - if ($param->{restore}) { - &$restore_openvz($private, $archive, $vmid, $param->{force}); - - # is this really needed? - my $cmd = ['vzctl', '--skiplock', '--quiet', 'set', $vmid, - '--applyconfig_map', 'name', '--save']; - run_command($cmd); - - # reload config - $conf = PVE::OpenVZ::load_config($vmid); - - # and initialize quota - my $disk_quota = $conf->{disk_quota}->{value}; - if (!defined($disk_quota) || ($disk_quota != 0)) { - $cmd = ['vzctl', '--skiplock', 'quotainit', $vmid]; - run_command($cmd); - } - - } else { - PVE::Tools::file_set_contents($basecfg_fn, $rawconf); - my $cmd = ['vzctl', '--skiplock', 'create', $vmid, - '--ostemplate', $archive, '--private', $private]; - run_command($cmd); - - # hack: vzctl '--userpasswd' starts the CT, but we want - # to avoid that for create - PVE::OpenVZ::set_rootpasswd($private, $password) - if defined($password); - } - - PVE::AccessControl::add_vm_to_pool($vmid, $pool) if $pool; - }; - - my $realcmd = sub { PVE::OpenVZ::lock_container($vmid, 1, $code); }; - - &$check_vmid_usage(); # first check before locking - - return $rpcenv->fork_worker($param->{restore} ? 'vzrestore' : 'vzcreate', - $vmid, $authuser, $realcmd); - }}); - -my $vm_config_perm_list = [ - 'VM.Config.Disk', - 'VM.Config.CPU', - 'VM.Config.Memory', - 'VM.Config.Network', - 'VM.Config.Options', - ]; - -__PACKAGE__->register_method({ - name => 'update_vm', - path => '{vmid}/config', - method => 'PUT', - protected => 1, - proxyto => 'node', - description => "Set virtual machine options.", - permissions => { - check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1], - }, - parameters => { - additionalProperties => 0, - properties => PVE::OpenVZ::json_config_properties( - { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - digest => { - type => 'string', - description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.', - maxLength => 40, - optional => 1, - } - }), - }, - returns => { type => 'null'}, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - my $digest = extract_param($param, 'digest'); - - die "no options specified\n" if !scalar(keys %$param); - - &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]); - - my $code = sub { - - my $conf = PVE::OpenVZ::load_config($vmid); - die "checksum missmatch (file change by other user?)\n" - if $digest && $digest ne $conf->{digest}; - - my $changes = PVE::OpenVZ::update_ovz_config($vmid, $conf, $param); - - return if scalar (@$changes) <= 0; - - my $cmd = ['vzctl', '--skiplock', 'set', $vmid, @$changes, '--save']; - - PVE::Cluster::log_msg('info', $authuser, "update CT $vmid: " . join(' ', @$changes)); - - run_command($cmd); - }; - - PVE::OpenVZ::lock_container($vmid, undef, $code); - - return undef; - }}); - -__PACKAGE__->register_method ({ - subclass => "PVE::API2::Firewall::CT", - path => '{vmid}/firewall', -}); - -__PACKAGE__->register_method({ - name => 'vmdiridx', - path => '{vmid}', - method => 'GET', - proxyto => 'node', - description => "Directory index", - permissions => { - user => 'all', - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'array', - items => { - type => "object", - properties => { - subdir => { type => 'string' }, - }, - }, - links => [ { rel => 'child', href => "{subdir}" } ], - }, - code => sub { - my ($param) = @_; - - # test if VM exists - my $conf = PVE::OpenVZ::load_config($param->{vmid}); - - my $res = [ - { subdir => 'config' }, - { subdir => 'status' }, - { subdir => 'vncproxy' }, - { subdir => 'spiceproxy' }, - { subdir => 'migrate' }, - { subdir => 'initlog' }, - { subdir => 'rrd' }, - { subdir => 'rrddata' }, - { subdir => 'firewall' }, - ]; - - return $res; - }}); - -__PACKAGE__->register_method({ - name => 'rrd', - path => '{vmid}/rrd', - method => 'GET', - protected => 1, # fixme: can we avoid that? - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], - }, - description => "Read VM RRD statistics (returns PNG)", - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - timeframe => { - description => "Specify the time frame you are interested in.", - type => 'string', - enum => [ 'hour', 'day', 'week', 'month', 'year' ], - }, - ds => { - description => "The list of datasources you want to display.", - type => 'string', format => 'pve-configid-list', - }, - cf => { - description => "The RRD consolidation function", - type => 'string', - enum => [ 'AVERAGE', 'MAX' ], - optional => 1, - }, - }, - }, - returns => { - type => "object", - properties => { - filename => { type => 'string' }, - }, - }, - code => sub { - my ($param) = @_; - - return PVE::Cluster::create_rrd_graph( - "pve2-vm/$param->{vmid}", $param->{timeframe}, - $param->{ds}, $param->{cf}); - - }}); - -__PACKAGE__->register_method({ - name => 'rrddata', - path => '{vmid}/rrddata', - method => 'GET', - protected => 1, # fixme: can we avoid that? - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], - }, - description => "Read VM RRD statistics", - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - timeframe => { - description => "Specify the time frame you are interested in.", - type => 'string', - enum => [ 'hour', 'day', 'week', 'month', 'year' ], - }, - cf => { - description => "The RRD consolidation function", - type => 'string', - enum => [ 'AVERAGE', 'MAX' ], - optional => 1, - }, - }, - }, - returns => { - type => "array", - items => { - type => "object", - properties => {}, - }, - }, - code => sub { - my ($param) = @_; - - return PVE::Cluster::create_rrd_data( - "pve2-vm/$param->{vmid}", $param->{timeframe}, $param->{cf}); - }}); - -__PACKAGE__->register_method({ - name => 'initlog', - path => '{vmid}/initlog', - method => 'GET', - protected => 1, - proxyto => 'node', - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], - }, - description => "Read init log.", - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - start => { - type => 'integer', - minimum => 0, - optional => 1, - }, - limit => { - type => 'integer', - minimum => 0, - optional => 1, - }, - }, - }, - returns => { - type => 'array', - items => { - type => "object", - properties => { - n => { - description=> "Line number", - type=> 'integer', - }, - t => { - description=> "Line text", - type => 'string', - } - } - } - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - my $authuser = $rpcenv->get_user(); - - my $vmid = $param->{vmid}; - - my $conf = PVE::OpenVZ::load_config($vmid); - - my $privatedir = PVE::OpenVZ::get_privatedir($conf, $vmid); - - my $logfn = "$privatedir/var/log/init.log"; - - my ($count, $lines) = PVE::Tools::dump_logfile($logfn, $param->{start}, $param->{limit}); - - $rpcenv->set_result_attrib('total', $count); - - return $lines; - }}); - -__PACKAGE__->register_method({ - name => 'vm_config', - path => '{vmid}/config', - method => 'GET', - proxyto => 'node', - description => "Get container configuration.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => "object", - properties => { - digest => { - type => 'string', - description => 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.', - } - }, - }, - code => sub { - my ($param) = @_; - - my $veconf = PVE::OpenVZ::load_config($param->{vmid}); - - # we only return selected/converted values - my $conf = { digest => $veconf->{digest} }; - - if ($veconf->{ostemplate} && $veconf->{ostemplate}->{value}) { - $conf->{ostemplate} = $veconf->{ostemplate}->{value}; - } - - my $stcfg = cfs_read_file("storage.cfg"); - - my ($sid, undef, $path) = &$get_container_storage($stcfg, $param->{vmid}, $veconf); - $conf->{storage} = $sid || $path; - - my $properties = PVE::OpenVZ::json_config_properties(); - - foreach my $k (keys %$properties) { - next if $k eq 'memory'; - next if $k eq 'swap'; - next if $k eq 'disk'; - - next if !$veconf->{$k}; - next if !defined($veconf->{$k}->{value}); - - if ($k eq 'description') { - $conf->{$k} = PVE::Tools::decode_text($veconf->{$k}->{value}); - } else { - $conf->{$k} = $veconf->{$k}->{value}; - } - } - - ($conf->{memory}, $conf->{swap}) = PVE::OpenVZ::ovz_config_extract_mem_swap($veconf, 1024*1024); - - my $diskspace = $veconf->{diskspace}->{bar} || LONG_MAX; - if ($diskspace == LONG_MAX) { - $conf->{disk} = 0; - } else { - $conf->{disk} = $diskspace/(1024*1024); - } - return $conf; - }}); - -__PACKAGE__->register_method({ - name => 'destroy_vm', - path => '{vmid}', - method => 'DELETE', - protected => 1, - proxyto => 'node', - description => "Destroy the container (also delete all uses files).", - permissions => { - check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $vmid = $param->{vmid}; - - # test if VM exists - my $conf = PVE::OpenVZ::load_config($param->{vmid}); - - my $realcmd = sub { - my $cmd = ['vzctl', 'destroy', $vmid ]; - - run_command($cmd); - - PVE::AccessControl::remove_vm_from_pool($vmid); - }; - - return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd); - }}); - -my $sslcert; - -__PACKAGE__->register_method ({ - name => 'vncproxy', - path => '{vmid}/vncproxy', - method => 'POST', - protected => 1, - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]], - }, - description => "Creates a TCP VNC proxy connections.", - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - websocket => { - optional => 1, - type => 'boolean', - description => "use websocket instead of standard VNC.", - }, - }, - }, - returns => { - additionalProperties => 0, - properties => { - user => { type => 'string' }, - ticket => { type => 'string' }, - cert => { type => 'string' }, - port => { type => 'integer' }, - upid => { type => 'string' }, - }, - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $vmid = $param->{vmid}; - my $node = $param->{node}; - - my $authpath = "/vms/$vmid"; - - my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath); - - $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192) - if !$sslcert; - - my $port = PVE::Tools::next_vnc_port(); - - my $remip; - - if ($node ne PVE::INotify::nodename()) { - $remip = PVE::Cluster::remote_node_ip($node); - } - - # NOTE: vncterm VNC traffic is already TLS encrypted, - # so we select the fastest chipher here (or 'none'?) - my $remcmd = $remip ? - ['/usr/bin/ssh', '-t', $remip] : []; - - my $shcmd = [ '/usr/bin/dtach', '-A', - "/var/run/dtach/vzctlconsole$vmid", - '-r', 'winch', '-z', - '/usr/sbin/vzctl', 'console', $vmid ]; - - my $realcmd = sub { - my $upid = shift; - - syslog ('info', "starting openvz vnc proxy $upid\n"); - - my $timeout = 10; - - my $cmd = ['/usr/bin/vncterm', '-rfbport', $port, - '-timeout', $timeout, '-authpath', $authpath, - '-perm', 'VM.Console']; - - if ($param->{websocket}) { - $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm - push @$cmd, '-notls', '-listen', 'localhost'; - } - - push @$cmd, '-c', @$remcmd, @$shcmd; - - run_command($cmd); - - return; - }; - - my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd); - - PVE::Tools::wait_for_vnc_port($port); - - return { - user => $authuser, - ticket => $ticket, - port => $port, - upid => $upid, - cert => $sslcert, - }; - }}); - -__PACKAGE__->register_method({ - name => 'vncwebsocket', - path => '{vmid}/vncwebsocket', - method => 'GET', - permissions => { - description => "You also need to pass a valid ticket (vncticket).", - check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]], - }, - description => "Opens a weksocket for VNC traffic.", - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - vncticket => { - description => "Ticket from previous call to vncproxy.", - type => 'string', - maxLength => 512, - }, - port => { - description => "Port number returned by previous vncproxy call.", - type => 'integer', - minimum => 5900, - maximum => 5999, - }, - }, - }, - returns => { - type => "object", - properties => { - port => { type => 'string' }, - }, - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $authpath = "/vms/$param->{vmid}"; - - PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $authuser, $authpath); - - my $port = $param->{port}; - - return { port => $port }; - }}); - -__PACKAGE__->register_method ({ - name => 'spiceproxy', - path => '{vmid}/spiceproxy', - method => 'POST', - protected => 1, - proxyto => 'node', - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]], - }, - description => "Returns a SPICE configuration to connect to the CT.", - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - proxy => get_standard_option('spice-proxy', { optional => 1 }), - }, - }, - returns => get_standard_option('remote-viewer-config'), - code => sub { - my ($param) = @_; - - my $vmid = $param->{vmid}; - my $node = $param->{node}; - my $proxy = $param->{proxy}; - - my $authpath = "/vms/$vmid"; - my $permissions = 'VM.Console'; - - my $shcmd = ['/usr/bin/dtach', '-A', - "/var/run/dtach/vzctlconsole$vmid", - '-r', 'winch', '-z', - '/usr/sbin/vzctl', 'console', $vmid]; - - my $title = "CT $vmid"; - - return PVE::API2Tools::run_spiceterm($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd); - }}); - -__PACKAGE__->register_method({ - name => 'vmcmdidx', - path => '{vmid}/status', - method => 'GET', - proxyto => 'node', - description => "Directory index", - permissions => { - user => 'all', - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'array', - items => { - type => "object", - properties => { - subdir => { type => 'string' }, - }, - }, - links => [ { rel => 'child', href => "{subdir}" } ], - }, - code => sub { - my ($param) = @_; - - # test if VM exists - my $conf = PVE::OpenVZ::load_config($param->{vmid}); - - my $res = [ - { subdir => 'current' }, - { subdir => 'ubc' }, - { subdir => 'start' }, - { subdir => 'stop' }, - ]; - - return $res; - }}); - -__PACKAGE__->register_method({ - name => 'vm_status', - path => '{vmid}/status/current', - method => 'GET', - proxyto => 'node', - protected => 1, # openvz /proc entries are only readable by root - description => "Get virtual machine status.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { type => 'object' }, - code => sub { - my ($param) = @_; - - # test if VM exists - my $conf = PVE::OpenVZ::load_config($param->{vmid}); - - my $vmstatus = PVE::OpenVZ::vmstatus($param->{vmid}); - my $status = $vmstatus->{$param->{vmid}}; - - $status->{ha} = PVE::Cluster::vm_is_ha_managed($param->{vmid}); - - return $status; - }}); - -__PACKAGE__->register_method({ - name => 'vm_user_beancounters', - path => '{vmid}/status/ubc', - method => 'GET', - proxyto => 'node', - protected => 1, # openvz /proc entries are only readable by root - description => "Get container user_beancounters.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'array', - items => { - type => "object", - properties => { - id => { type => 'string' }, - held => { type => 'number' }, - maxheld => { type => 'number' }, - bar => { type => 'number' }, - lim => { type => 'number' }, - failcnt => { type => 'number' }, - }, - }, - }, - code => sub { - my ($param) = @_; - - # test if VM exists - my $conf = PVE::OpenVZ::load_config($param->{vmid}); - - my $ubchash = PVE::OpenVZ::read_user_beancounters(); - my $ubc = $ubchash->{$param->{vmid}} || {}; - delete $ubc->{failcntsum}; - - return PVE::RESTHandler::hash_to_array($ubc, 'id'); - }}); - -__PACKAGE__->register_method({ - name => 'vm_start', - path => '{vmid}/status/start', - method => 'POST', - protected => 1, - proxyto => 'node', - description => "Start the container.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - die "CT $vmid already running\n" if PVE::OpenVZ::check_running($vmid); - - if (PVE::Cluster::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') { - - my $hacmd = sub { - my $upid = shift; - - my $service = "pvevm:$vmid"; - - my $cmd = ['clusvcadm', '-e', $service, '-m', $node]; - - print "Executing HA start for CT $vmid\n"; - - PVE::Tools::run_command($cmd); - - return; - }; - - return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd); - - } else { - - my $realcmd = sub { - my $upid = shift; - - syslog('info', "starting CT $vmid: $upid\n"); - - my $veconf = PVE::OpenVZ::load_config($vmid); - my $stcfg = cfs_read_file("storage.cfg"); - if (my $sid = &$get_container_storage($stcfg, $vmid, $veconf)) { - PVE::Storage::activate_storage($stcfg, $sid); - } - - my $vzconf = PVE::OpenVZ::read_global_vz_config(); - - # make sure mount point is there (see bug #276) - my $root = PVE::OpenVZ::get_rootdir($veconf, $vmid); - mkpath $root || die "unable to create root dir '$root'"; - - my $cmd = ['vzctl', 'start', $vmid]; - - run_command($cmd); - - return; - }; - - return $rpcenv->fork_worker('vzstart', $vmid, $authuser, $realcmd); - } - }}); - -__PACKAGE__->register_method({ - name => 'vm_stop', - path => '{vmid}/status/stop', - method => 'POST', - protected => 1, - proxyto => 'node', - description => "Stop the container.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - die "CT $vmid not running\n" if !PVE::OpenVZ::check_running($vmid); - - if (PVE::Cluster::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') { - - my $hacmd = sub { - my $upid = shift; - - my $service = "pvevm:$vmid"; - - my $cmd = ['clusvcadm', '-d', $service]; - - print "Executing HA stop for CT $vmid\n"; - - PVE::Tools::run_command($cmd); - - return; - }; - - return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd); - - } else { - - my $realcmd = sub { - my $upid = shift; - - syslog('info', "stoping CT $vmid: $upid\n"); - - my $cmd = ['vzctl', 'stop', $vmid, '--fast']; - run_command($cmd); - - return; - }; - - return $rpcenv->fork_worker('vzstop', $vmid, $authuser, $realcmd); - } - }}); - -__PACKAGE__->register_method({ - name => 'vm_mount', - path => '{vmid}/status/mount', - method => 'POST', - protected => 1, - proxyto => 'node', - description => "Mounts container private area.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - die "CT $vmid is running\n" if PVE::OpenVZ::check_running($vmid); - - my $realcmd = sub { - my $upid = shift; - - syslog('info', "mount CT $vmid: $upid\n"); - - my $cmd = ['vzctl', 'mount', $vmid]; - - run_command($cmd); - - return; - }; - - return $rpcenv->fork_worker('vzmount', $vmid, $authuser, $realcmd); - }}); - -__PACKAGE__->register_method({ - name => 'vm_umount', - path => '{vmid}/status/umount', - method => 'POST', - protected => 1, - proxyto => 'node', - description => "Unmounts container private area.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - die "CT $vmid is running\n" if PVE::OpenVZ::check_running($vmid); - - my $realcmd = sub { - my $upid = shift; - - syslog('info', "umount CT $vmid: $upid\n"); - - my $cmd = ['vzctl', 'umount', $vmid]; - - run_command($cmd); - - return; - }; - - return $rpcenv->fork_worker('vzumount', $vmid, $authuser, $realcmd); - }}); - -__PACKAGE__->register_method({ - name => 'vm_shutdown', - path => '{vmid}/status/shutdown', - method => 'POST', - protected => 1, - proxyto => 'node', - description => "Shutdown the container.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - timeout => { - description => "Wait maximal timeout seconds.", - type => 'integer', - minimum => 0, - optional => 1, - default => 60, - }, - forceStop => { - description => "Make sure the Container stops.", - type => 'boolean', - optional => 1, - default => 0, - } - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - my $timeout = extract_param($param, 'timeout'); - - die "CT $vmid not running\n" if !PVE::OpenVZ::check_running($vmid); - - my $realcmd = sub { - my $upid = shift; - - syslog('info', "shutdown CT $vmid: $upid\n"); - - my $cmd = ['vzctl', 'stop', $vmid]; - - $timeout = 60 if !defined($timeout); - - eval { run_command($cmd, timeout => $timeout); }; - my $err = $@; - return if !$err; - - die $err if !$param->{forceStop}; - - warn "shutdown failed - forcing stop now\n"; - - push @$cmd, '--fast'; - run_command($cmd); - - return; - }; - - my $upid = $rpcenv->fork_worker('vzshutdown', $vmid, $authuser, $realcmd); - - return $upid; - }}); - -__PACKAGE__->register_method({ - name => 'vm_suspend', - path => '{vmid}/status/suspend', - method => 'POST', - protected => 1, - proxyto => 'node', - description => "Suspend the container.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - die "CT $vmid not running\n" if !PVE::OpenVZ::check_running($vmid); - - my $realcmd = sub { - my $upid = shift; - - syslog('info', "suspend CT $vmid: $upid\n"); - - PVE::OpenVZ::vm_suspend($vmid); - - return; - }; - - my $upid = $rpcenv->fork_worker('vzsuspend', $vmid, $authuser, $realcmd); - - return $upid; - }}); - -__PACKAGE__->register_method({ - name => 'vm_resume', - path => '{vmid}/status/resume', - method => 'POST', - protected => 1, - proxyto => 'node', - description => "Resume the container.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $node = extract_param($param, 'node'); - - my $vmid = extract_param($param, 'vmid'); - - die "CT $vmid already running\n" if PVE::OpenVZ::check_running($vmid); - - my $realcmd = sub { - my $upid = shift; - - syslog('info', "resume CT $vmid: $upid\n"); - - PVE::OpenVZ::vm_resume($vmid); - - return; - }; - - my $upid = $rpcenv->fork_worker('vzresume', $vmid, $authuser, $realcmd); - - return $upid; - }}); - -__PACKAGE__->register_method({ - name => 'migrate_vm', - path => '{vmid}/migrate', - method => 'POST', - protected => 1, - proxyto => 'node', - description => "Migrate the container to another node. Creates a new migration task.", - permissions => { - check => ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]], - }, - parameters => { - additionalProperties => 0, - properties => { - node => get_standard_option('pve-node'), - vmid => get_standard_option('pve-vmid'), - target => get_standard_option('pve-node', { description => "Target node." }), - online => { - type => 'boolean', - description => "Use online/live migration.", - optional => 1, - }, - }, - }, - returns => { - type => 'string', - description => "the task ID.", - }, - code => sub { - my ($param) = @_; - - my $rpcenv = PVE::RPCEnvironment::get(); - - my $authuser = $rpcenv->get_user(); - - my $target = extract_param($param, 'target'); - - my $localnode = PVE::INotify::nodename(); - raise_param_exc({ target => "target is local node."}) if $target eq $localnode; - - PVE::Cluster::check_cfs_quorum(); - - PVE::Cluster::check_node_exists($target); - - my $targetip = PVE::Cluster::remote_node_ip($target); - - my $vmid = extract_param($param, 'vmid'); - - # test if VM exists - PVE::OpenVZ::load_config($vmid); - - # try to detect errors early - if (PVE::OpenVZ::check_running($vmid)) { - die "cant migrate running container without --online\n" - if !$param->{online}; - } - - if (PVE::Cluster::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') { - - my $hacmd = sub { - my $upid = shift; - - my $service = "pvevm:$vmid"; - - my $cmd = ['clusvcadm', '-M', $service, '-m', $target]; - - print "Executing HA migrate for CT $vmid to node $target\n"; - - PVE::Tools::run_command($cmd); - - return; - }; - - return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd); - - } else { - - my $realcmd = sub { - my $upid = shift; - - PVE::OpenVZMigrate->migrate($target, $targetip, $vmid, $param); - - return; - }; - - return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $realcmd); - } - }}); - -1; diff --git a/PVE/Makefile b/PVE/Makefile index 0aa57962a..bb1892026 100644 --- a/PVE/Makefile +++ b/PVE/Makefile @@ -11,8 +11,6 @@ PERLSOURCE = \ NoVncIndex.pm \ HTTPServer.pm \ REST.pm \ - OpenVZ.pm \ - OpenVZMigrate.pm \ APLInfo.pm \ AutoBalloon.pm \ CephTools.pm \ diff --git a/PVE/OpenVZ.pm b/PVE/OpenVZ.pm deleted file mode 100644 index 71bc2a5a6..000000000 --- a/PVE/OpenVZ.pm +++ /dev/null @@ -1,1305 +0,0 @@ -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::Tools qw(run_command extract_param $IPV6RE $IPV4RE); -use PVE::ProcFSTools; -use PVE::Cluster qw(cfs_register_file cfs_read_file); -use PVE::SafeSyslog; -use PVE::INotify; -use PVE::JSONSchema; -use Digest::SHA; -use Encode; - -use constant SCRIPT_EXT => qw (start stop mount umount premount postumount); - -my $cpuinfo = PVE::ProcFSTools::read_cpuinfo(); -my $nodename = PVE::INotify::nodename(); -my $global_vzconf = read_global_vz_config(); -my $res_unlimited = LONG_MAX; - -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; -} - -sub cfs_config_path { - my ($vmid, $node) = @_; - - $node = $nodename if !$node; - return "nodes/$node/openvz/$vmid.conf"; -} - -sub config_file { - my ($vmid, $node) = @_; - - my $cfspath = cfs_config_path($vmid, $node); - return "/etc/pve/$cfspath"; -} - -sub load_config { - my ($vmid) = @_; - - my $cfspath = cfs_config_path($vmid); - - my $conf = PVE::Cluster::cfs_read_file($cfspath); - die "container $vmid does not exists\n" if !defined($conf); - - return $conf; -} - -sub check_mounted { - my ($conf, $vmid) = @_; - - my $root = get_rootdir($conf, $vmid); - return (-d "$root/etc" || -d "$root/proc"); -} - -# warning: this is slow -sub check_running { - my ($vmid) = @_; - - if (my $fh = new IO::File ("/proc/vz/vestat", "r")) { - while (defined (my $line = <$fh>)) { - if ($line =~ m/^\s*(\d+)\s+/) { - if ($vmid == $1) { - close($fh); - return 1; - } - } - } - close($fh); - } - return undef; -} - -sub get_privatedir { - my ($conf, $vmid) = @_; - - my $private = $global_vzconf->{privatedir}; - if ($conf->{ve_private} && $conf->{ve_private}->{value}) { - $private = $conf->{ve_private}->{value}; - } - $private =~ s/\$VEID/$vmid/; - - return $private; -} - -sub get_rootdir { - my ($conf, $vmid) = @_; - - my $root = $global_vzconf->{rootdir}; - if ($conf && $conf->{ve_root} && $conf->{ve_root}->{value}) { - $root = $conf->{ve_root}->{value}; - } - $root =~ s/\$VEID/$vmid/; - - return $root; -} - -sub get_disk_quota { - my ($conf) = @_; - - my $disk_quota = $global_vzconf->{disk_quota}; - if ($conf->{disk_quota} && defined($conf->{disk_quota}->{value})) { - $disk_quota = $conf->{disk_quota}->{value}; - } - - return $disk_quota; -} - -sub read_user_beancounters { - my $ubc = {}; - - if (my $fh = IO::File->new ("/proc/bc/resources", "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+)$|) { - $vmid = $2 if defined($2); - next if !defined($vmid); - my ($name, $held, $maxheld, $bar, $lim, $failcnt) = (lc($3), $4, $5, $6, $7, $8); - next if $name eq 'dummy'; - $ubc->{$vmid}->{failcntsum} += $failcnt; - $ubc->{$vmid}->{$name} = { - held => $held, - maxheld => $maxheld, - bar => $bar, - lim => $lim, - failcnt => $failcnt, - }; - } - } - close($fh); - } - - return $ubc; -} - -sub read_container_network_usage { - my ($vmid) = @_; - - my $recv = 0; - my $trmt = 0; - - my $netparser = sub { - my $line = shift; - if ($line =~ m/^\s*(.*):\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)\s+/) { - return if $1 eq 'lo'; - $recv += $2; - $trmt += $3; - } - }; - - # fixme: can we get that info directly (with vzctl exec)? - my $cmd = ['/usr/sbin/vzctl', 'exec', $vmid, '/bin/cat', '/proc/net/dev']; - eval { run_command($cmd, outfunc => $netparser); }; - my $err = $@; - syslog('err', $err) if $err; - - return ($recv, $trmt); -}; - -sub read_container_blkio_stat { - my ($vmid) = @_; - - my $read = 0; - my $write = 0; - - my $filename = "/proc/vz/beancounter/$vmid/blkio.io_service_bytes"; - if (my $fh = IO::File->new ($filename, "r")) { - - while (defined (my $line = <$fh>)) { - if ($line =~ m/^\S+\s+Read\s+(\d+)$/) { - $read += $1; - } elsif ($line =~ m/^\S+\s+Write\s+(\d+)$/) { - $write += $1; - } - } - } - - return ($read, $write); -}; - -my $last_proc_vestat = {}; - -sub vmstatus { - my ($opt_vmid) = @_; - - my $list = $opt_vmid ? { $opt_vmid => { type => 'openvz' }} : config_list(); - - my $cpucount = $cpuinfo->{cpus} || 1; - - foreach my $vmid (keys %$list) { - next if $opt_vmid && ($vmid ne $opt_vmid); - - my $d = $list->{$vmid}; - $d->{status} = 'stopped'; - - my $cfspath = cfs_config_path($vmid); - if (my $conf = PVE::Cluster::cfs_read_file($cfspath)) { - $d->{name} = $conf->{hostname}->{value} || "CT$vmid"; - $d->{name} =~ s/[\s]//g; - - $d->{cpus} = $conf->{cpus}->{value} || 1; - $d->{cpus} = $cpucount if $d->{cpus} > $cpucount; - - $d->{disk} = 0; - $d->{maxdisk} = int($conf->{diskspace}->{bar} * 1024); - - $d->{mem} = 0; - $d->{swap} = 0; - - ($d->{maxmem}, $d->{maxswap}) = ovz_config_extract_mem_swap($conf); - - $d->{nproc} = 0; - $d->{failcnt} = 0; - - $d->{uptime} = 0; - $d->{cpu} = 0; - - $d->{netout} = 0; - $d->{netin} = 0; - - $d->{diskread} = 0; - $d->{diskwrite} = 0; - - if (my $ip = $conf->{ip_address}->{value}) { - $ip =~ s/,;/ /g; - $d->{ip} = (split(/\s+/, $ip))[0]; - } else { - $d->{ip} = '-'; - } - - $d->{status} = 'mounted' if check_mounted($conf, $vmid); - - } else { - delete $list->{$vmid}; - } - } - - my $maxpages = ($res_unlimited / 4096); - my $ubchash = read_user_beancounters(); - foreach my $vmid (keys %$ubchash) { - my $d = $list->{$vmid}; - my $ubc = $ubchash->{$vmid}; - if ($d && defined($d->{status}) && $ubc) { - $d->{failcnt} = $ubc->{failcntsum}; - $d->{mem} = $ubc->{physpages}->{held} * 4096; - if ($ubc->{swappages}->{held} < $maxpages) { - $d->{swap} = $ubc->{swappages}->{held} * 4096 - } - $d->{nproc} = $ubc->{numproc}->{held}; - } - } - - if (my $fh = IO::File->new ("/proc/vz/vzquota", "r")) { - while (defined (my $line = <$fh>)) { - if ($line =~ m|^(\d+):\s+\S+/private/\d+$|) { - my $vmid = $1; - my $d = $list->{$vmid}; - if ($d && defined($d->{status})) { - $line = <$fh>; - if ($line =~ m|^\s*1k-blocks\s+(\d+)\s+(\d+)\s|) { - $d->{disk} = int ($1 * 1024); - $d->{maxdisk} = int ($2 * 1024); - } - } - } - } - close($fh); - } - - # Note: OpenVZ does not use POSIX::_SC_CLK_TCK - my $hz = 1000; - - # see http://wiki.openvz.org/Vestat - 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 $vmid = $1; - my $user = $2; - my $nice = $3; - my $system = $4; - my $ut = $5; - my $sum = $8*$cpucount; # uptime in jiffies * cpus = available jiffies - my $used = $9; # used time in jiffies - - my $uptime = int ($ut / $hz); - - my $d = $list->{$vmid}; - next if !($d && defined($d->{status})); - - $d->{status} = 'running'; - $d->{uptime} = $uptime; - - if (!defined ($last_proc_vestat->{$vmid}) || - ($last_proc_vestat->{$vmid}->{sum} > $sum)) { - $last_proc_vestat->{$vmid} = { used => 0, sum => 0, cpu => 0 }; - } - - my $diff = $sum - $last_proc_vestat->{$vmid}->{sum}; - - if ($diff > 1000) { # don't update too often - my $useddiff = $used - $last_proc_vestat->{$vmid}->{used}; - my $cpu = (($useddiff/$diff) * $cpucount) / $d->{cpus}; - $last_proc_vestat->{$vmid}->{sum} = $sum; - $last_proc_vestat->{$vmid}->{used} = $used; - $last_proc_vestat->{$vmid}->{cpu} = $d->{cpu} = $cpu; - } else { - $d->{cpu} = $last_proc_vestat->{$vmid}->{cpu}; - } - } - } - close($fh); - } - - foreach my $vmid (keys %$list) { - my $d = $list->{$vmid}; - next if !$d || !$d->{status} || $d->{status} ne 'running'; - ($d->{netin}, $d->{netout}) = read_container_network_usage($vmid); - ($d->{diskread}, $d->{diskwrite}) = read_container_blkio_stat($vmid); - } - - 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 => 0, - default => 512, - }, - disk => { - optional => 1, - type => 'number', - description => "Amount of disk space for the VM in GB. A zero indicates no limits.", - minimum => 0, - 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. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.", - }, - nameserver => { - optional => 1, - type => 'string', - description => "Sets DNS server IP address for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.", - }, - 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 -sub read_global_vz_config { - - 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', - disk_quota => 1, - }; - - my $filename = "/etc/vz/vz.conf"; - - return $res if ! -f $filename; - - my $data = PVE::Tools::file_get_contents($filename); - - 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; - } - if ($data =~ m/^\s*DISK_QUOTA=(no|false|off|0)$/m) { - $res->{disk_quota} = 0; - } - - return $res; -}; - -sub parse_netif { - my ($data, $vmid) = @_; - - my $res = {}; - return $res if !$data; - - my $host_ifnames = {}; - - my $find_next_hostif_name = sub { - for (my $i = 0; $i < 100; $i++) { - my $name = "veth${vmid}.$i"; - if (!$host_ifnames->{$name}) { - $host_ifnames->{$name} = 1; - return $name; - } - } - - die "unable to find free host_ifname"; # should not happen - }; - - foreach my $iface (split (/;/, $data)) { - my $d = {}; - foreach my $pv (split (/,/, $iface)) { - if ($pv =~ m/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(.+)$/) { - if ($1 eq 'host_ifname') { - $d->{$1} = $2; - $host_ifnames->{$2} = $1; - } elsif ($1 eq 'mac_filter') { - $d->{$1} = parse_boolean('mac_filter', $2); - } else { - $d->{$1} = $2; - } - } - } - if ($d->{ifname}) { - $d->{mac} = PVE::Tools::random_ether_addr() if !$d->{mac}; - $d->{host_mac} = PVE::Tools::random_ether_addr() if !$d->{host_mac}; - $d->{raw} = print_netif($d); - $res->{$d->{ifname}} = $d; - } else { - return undef; - } - } - - foreach my $iface (keys %$res) { - my $d = $res->{$iface}; - if ($vmid && !$d->{host_ifname}) { - $d->{host_ifname} = &$find_next_hostif_name($iface); - } - } - - return $res; -} - -sub print_netif { - my $net = shift; - - my $res = "ifname=$net->{ifname}"; - $res .= ",mac=$net->{mac}" if $net->{mac}; - $res .= ",host_ifname=$net->{host_ifname}" if $net->{host_ifname}; - $res .= ",host_mac=$net->{host_mac}" if $net->{host_mac}; - $res .= ",bridge=$net->{bridge}" if $net->{bridge}; - - if (defined($net->{mac_filter}) && !$net->{mac_filter}) { - $res .= ",mac_filter=off"; # 'on' is the default - } - - 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 = $2 ? 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 => 1 } if $text =~ m/^(yes|true|on|1)$/i; - return { value => 0 } 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, - - disk_quota => \&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::SHA::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})) { - if ($confdesc->{$key} && $confdesc->{$key}->{type} eq 'boolean') { - my $txt = $data->{value} ? 'yes' : 'no'; - $text .= uc($key) . "=\"$txt\"\n"; - } else { - $text .= uc($key) . "=\"$data->{value}\"\n"; - } - } elsif (defined($data->{bar})) { - my $tmp = format_res_bar_lim($key, $data); - $text .= uc($key) . "=\"$tmp\"\n"; - } -} - -sub ovz_config_extract_mem_swap { - my ($veconf, $unit) = @_; - - $unit = 1 if !$unit; - - my ($mem, $swap) = (int((512*1024*1024 + $unit - 1)/$unit), 0); - - my $maxpages = ($res_unlimited / 4096); - - if ($veconf->{swappages}) { - if ($veconf->{physpages} && $veconf->{physpages}->{lim} && - ($veconf->{physpages}->{lim} < $maxpages)) { - $mem = int(($veconf->{physpages}->{lim} * 4096 + $unit - 1) / $unit); - } - if ($veconf->{swappages}->{lim} && ($veconf->{swappages}->{lim} < $maxpages)) { - $swap = int (($veconf->{swappages}->{lim} * 4096 + $unit - 1) / $unit); - } - } else { - if ($veconf->{vmguarpages} && $veconf->{vmguarpages}->{bar} && - ($veconf->{vmguarpages}->{bar} < $maxpages)) { - $mem = int(($veconf->{vmguarpages}->{bar} * 4096 + $unit - 1) / $unit); - } - } - - return ($mem, $swap); -} - -sub update_ovz_config { - my ($vmid, $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}) - if $veconf->{$name} && defined($veconf->{$name}->{bar}); - my $new = format_res_bar_lim($name, { bar => $bar, lim => $lim }); - if (!$old || ($old ne $new)) { - $veconf->{$name}->{bar} = $bar; - $veconf->{$name}->{lim} = $lim; - push @$changes, "--$name", $new; - } - }; - - my ($mem, $swap) = ovz_config_extract_mem_swap($veconf, 1024*1024); - my $disk = ($veconf->{diskspace}->{bar} || $res_unlimited) / (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; - - 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 || ($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} ? 1 : 0; - my $oldvalue = $veconf->{$name}->{value}; - if (!defined($oldvalue) || ($oldvalue ne $newvalue)) { - $veconf->{$name}->{value} = $newvalue; - push @$changes, "--$name", $newvalue ? 'yes' : 'no'; - } - }; - - 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!^(?:$IPV6RE|$IPV4RE)(?:/\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}, $vmid); - } - my $newif = parse_netif($param->{netif}, $vmid); - - 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 .= print_netif($newif->{$ifname}); - - my $ifadd = $ifname; - $ifadd .= $newif->{$ifname}->{mac} ? ",$newif->{$ifname}->{mac}" : ','; - $ifadd .= $newif->{$ifname}->{host_ifname} ? ",$newif->{$ifname}->{host_ifname}" : ','; - $ifadd .= $newif->{$ifname}->{host_mac} ? ",$newif->{$ifname}->{host_mac}" : ','; - $ifadd .= $newif->{$ifname}->{bridge} ? ",$newif->{$ifname}->{bridge}" : ''; - - # not possible with current vzctl - #$ifadd .= $newif->{$ifname}->{mac_filter} ? ",$newif->{$ifname}->{mac_filter}" : ''; - - if (!$ifaces->{$ifname} || ($ifaces->{$ifname}->{raw} ne $newif->{$ifname}->{raw})) { - push @$changes, '--netif_add', $ifadd; - } - } - $veconf->{netif}->{value} = $newvalue; - } - - if (defined($param->{'nameserver'})) { - # remove duplicates - my $nshash = {}; - my $newvalue = ''; - foreach my $ns (PVE::Tools::split_list($param->{'nameserver'})) { - if (!$nshash->{$ns}) { - push @$changes, '--nameserver', $ns; - $nshash->{$ns} = 1; - $newvalue .= $newvalue ? " $ns" : $ns; - } - } - $veconf->{'nameserver'}->{value} = $newvalue if $newvalue; - } - - # 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 create_lock_manager { - my ($max) = @_; - - return LockFile::Simple->make(-format => '%f', - -autoclean => 1, - -max => defined($max) ? $max : 60, - -delay => 1, - -stale => 1, - -nfs => 0); -} - -sub lock_container { - my ($vmid, $max, $code, @param) = @_; - - my $filename = $global_vzconf->{lockdir} . "/${vmid}.lck"; - my $lock; - my $res; - - eval { - - my $lockmgr = create_lock_manager($max); - - $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 vm_suspend { - my ($vmid) = @_; - - my $cmd = ['vzctl', 'chkpnt', $vmid]; - - eval { run_command($cmd); }; - if (my $err = $@) { - syslog("err", "CT $vmid suspend failed - $err"); - die $err; - } -} - -sub vm_resume { - my ($vmid) = @_; - - my $cmd = ['vzctl', 'restore', $vmid]; - - eval { run_command($cmd); }; - if (my $err = $@) { - syslog("err", "CT $vmid resume failed - $err"); - die $err; - } -} - -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 = )) { - $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 ($privatedir, $opt_rootpasswd) = @_; - - my $pwfile = "$privatedir/etc/passwd"; - - return if ! -f $pwfile; - - my $shadow = "$privatedir/etc/shadow"; - - if ($opt_rootpasswd !~ m/^\$/) { - my $time = substr (Digest::SHA::sha1_base64 (time), 0, 8); - $opt_rootpasswd = crypt(encode("utf8", $opt_rootpasswd), "\$1\$$time\$"); - }; - - if (-f $shadow) { - replacepw ($shadow, $opt_rootpasswd); - replacepw ($pwfile, 'x'); - } else { - replacepw ($pwfile, $opt_rootpasswd); - } -} diff --git a/PVE/OpenVZMigrate.pm b/PVE/OpenVZMigrate.pm deleted file mode 100644 index 8f261ef1f..000000000 --- a/PVE/OpenVZMigrate.pm +++ /dev/null @@ -1,335 +0,0 @@ -package PVE::OpenVZMigrate; - -use strict; -use warnings; -use PVE::AbstractMigrate; -use File::Basename; -use File::Copy; -use PVE::Tools; -use PVE::INotify; -use PVE::Cluster; -use PVE::Storage; -use PVE::OpenVZ; - -use base qw(PVE::AbstractMigrate); - -# fixme: lock VM on target node - -sub lock_vm { - my ($self, $vmid, $code, @param) = @_; - - return PVE::OpenVZ::lock_container($vmid, undef, $code, @param); -} - -sub prepare { - my ($self, $vmid) = @_; - - my $online = $self->{opts}->{online}; - - $self->{storecfg} = PVE::Storage::config(); - $self->{vzconf} = PVE::OpenVZ::read_global_vz_config(), - - # test is VM exist - my $conf = $self->{vmconf} = PVE::OpenVZ::load_config($vmid); - - my $path = PVE::OpenVZ::get_privatedir($conf, $vmid); - my ($vtype, $volid) = PVE::Storage::path_to_volume_id($self->{storecfg}, $path); - my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1) if $volid; - - die "can't determine assigned storage\n" if !$storage; - - # check if storage is available on both nodes - my $scfg = PVE::Storage::storage_check_node($self->{storecfg}, $storage); - PVE::Storage::storage_check_node($self->{storecfg}, $storage, $self->{node}); - - # we simply use the backup dir to store temporary dump files - # Note: this is on shared storage if the storage is 'shared' - $self->{dumpdir} = PVE::Storage::get_backup_dir($self->{storecfg}, $storage); - - PVE::Storage::activate_volumes($self->{storecfg}, [ $volid ]); - - $self->{storage} = $storage; - $self->{privatedir} = $path; - - $self->{rootdir} = PVE::OpenVZ::get_rootdir($conf, $vmid); - - $self->{shared} = $scfg->{shared}; - - my $running = 0; - if (PVE::OpenVZ::check_running($vmid)) { - die "cant migrate running container without --online\n" if !$online; - $running = 1; - } - - # fixme: test if VM uses local resources - - # test ssh connection - my $cmd = [ @{$self->{rem_ssh}}, '/bin/true' ]; - eval { $self->cmd_quiet($cmd); }; - die "Can't connect to destination address using public key\n" if $@; - - if ($running) { - - # test if OpenVZ is running - $cmd = [ @{$self->{rem_ssh}}, '/etc/init.d/vz status' ]; - eval { $self->cmd_quiet($cmd); }; - die "OpenVZ is not running on the target machine\n" if $@; - - # test if CPT modules are loaded for online migration - die "vzcpt module is not loaded\n" if ! -f '/proc/cpt'; - - $cmd = [ @{$self->{rem_ssh}}, 'test -f /proc/rst' ]; - eval { $self->cmd_quiet($cmd); }; - die "vzrst module is not loaded on the target machine\n" if $@; - } - - # fixme: do we want to test if IPs exists on target node? - - return $running; -} - -sub phase1 { - my ($self, $vmid) = @_; - - $self->log('info', "starting migration of CT $self->{vmid} to node '$self->{node}' ($self->{nodeip})"); - - my $conf = $self->{vmconf}; - - if ($self->{running}) { - $self->log('info', "container is running - using online migration"); - } - - my $cmd = [ @{$self->{rem_ssh}}, 'mkdir', '-p', $self->{rootdir} ]; - $self->cmd_quiet($cmd, errmsg => "Failed to make container root directory"); - - my $privatedir = $self->{privatedir}; - - if (!$self->{shared}) { - - $cmd = [ @{$self->{rem_ssh}}, 'mkdir', '-p', $privatedir ]; - $self->cmd_quiet($cmd, errmsg => "Failed to make container private directory"); - - $self->{undo_private} = $privatedir; - - $self->log('info', "starting rsync phase 1"); - my $basedir = dirname($privatedir); - $cmd = [ @{$self->{rsync_cmd}}, '--sparse', $privatedir, "root\@$self->{nodeip}:$basedir" ]; - $self->cmd($cmd, errmsg => "Failed to sync container private area"); - } else { - $self->log('info', "container data is on shared storage '$self->{storage}'"); - } - - my $conffile = PVE::OpenVZ::config_file($vmid); - my $newconffile = PVE::OpenVZ::config_file($vmid, $self->{node}); - - my $srccfgdir = dirname($conffile); - my $newcfgdir = dirname($newconffile); - foreach my $s (PVE::OpenVZ::SCRIPT_EXT) { - my $scriptfn = "${vmid}.$s"; - my $srcfn = "$srccfgdir/$scriptfn"; - next if ! -f $srcfn; - my $dstfn = "$newcfgdir/$scriptfn"; - copy($srcfn, $dstfn) || die "copy '$srcfn' to '$dstfn' failed - $!\n"; - } - - if ($self->{running}) { - # fixme: save state and quota - $self->log('info', "start live migration - suspending container"); - $cmd = [ 'vzctl', '--skiplock', 'chkpnt', $vmid, '--suspend' ]; - $self->cmd_quiet($cmd, errmsg => "Failed to suspend container"); - - $self->{undo_suspend} = 1; - - $self->log('info', "dump container state"); - $self->{dumpfile} = "$self->{dumpdir}/dump.$vmid"; - $cmd = [ 'vzctl', '--skiplock', 'chkpnt', $vmid, '--dump', '--dumpfile', $self->{dumpfile} ]; - $self->cmd_quiet($cmd, errmsg => "Failed to dump container state"); - - if (!$self->{shared}) { - $self->log('info', "copy dump file to target node"); - $self->{undo_copy_dump} = 1; - $cmd = [ @{$self->{scp_cmd}}, $self->{dumpfile}, "root\@$self->{nodeip}:$self->{dumpfile}"]; - $self->cmd_quiet($cmd, errmsg => "Failed to copy dump file"); - - $self->log('info', "starting rsync (2nd pass)"); - my $basedir = dirname($privatedir); - $cmd = [ @{$self->{rsync_cmd}}, $privatedir, "root\@$self->{nodeip}:$basedir" ]; - $self->cmd($cmd, errmsg => "Failed to sync container private area"); - } - } else { - if (PVE::OpenVZ::check_mounted($conf, $vmid)) { - $self->log('info', "unmounting container"); - $cmd = [ 'vzctl', '--skiplock', 'umount', $vmid ]; - $self->cmd_quiet($cmd, errmsg => "Failed to umount container"); - } - } - - my $disk_quota = PVE::OpenVZ::get_disk_quota($conf); - if (!defined($disk_quota) || ($disk_quota != 0)) { - $disk_quota = $self->{disk_quota} = 1; - - $self->log('info', "dump 2nd level quota"); - $self->{quotadumpfile} = "$self->{dumpdir}/quotadump.$vmid"; - $cmd = "vzdqdump $vmid -U -G -T > " . PVE::Tools::shellquote($self->{quotadumpfile}); - $self->cmd_quiet($cmd, errmsg => "Failed to dump 2nd level quota"); - - if (!$self->{shared}) { - $self->log('info', "copy 2nd level quota to target node"); - $self->{undo_copy_quota_dump} = 1; - $cmd = [@{$self->{scp_cmd}}, $self->{quotadumpfile}, - "root\@$self->{nodeip}:$self->{quotadumpfile}"]; - $self->cmd_quiet($cmd, errmsg => "Failed to copy 2nd level quota dump"); - } - } - - # everythin copied - make sure container is stoped - # fixme_ do we need to start on the other node first? - if ($self->{running}) { - delete $self->{undo_suspend}; - $cmd = [ 'vzctl', '--skiplock', 'chkpnt', $vmid, '--kill' ]; - $self->cmd_quiet($cmd, errmsg => "Failed to kill container"); - $cmd = [ 'vzctl', '--skiplock', 'umount', $vmid ]; - sleep(1); # hack: wait - else there are open files - $self->cmd_quiet($cmd, errmsg => "Failed to umount container"); - } - - # move config - die "Failed to move config to node '$self->{node}' - rename failed: $!\n" - if !rename($conffile, $newconffile); -} - -sub phase1_cleanup { - my ($self, $vmid, $err) = @_; - - $self->log('info', "aborting phase 1 - cleanup resources"); - - my $conf = $self->{vmconf}; - - if ($self->{undo_suspend}) { - my $cmd = [ 'vzctl', '--skiplock', 'chkpnt', $vmid, '--resume' ]; - $self->cmd_logerr($cmd, errmsg => "Failed to resume container"); - } - - if ($self->{undo_private}) { - $self->log('info', "removing copied files on target node"); - my $cmd = [ @{$self->{rem_ssh}}, 'rm', '-rf', $self->{undo_private} ]; - $self->cmd_logerr($cmd, errmsg => "Failed to remove copied files"); - } - - # fixme: that seem to be very dangerous and not needed - #my $cmd = [ @{$self->{rem_ssh}}, 'rm', '-rf', $self->{rootdir} ]; - #eval { $self->cmd_quiet($cmd); }; - - my $newconffile = PVE::OpenVZ::config_file($vmid, $self->{node}); - my $newcfgdir = dirname($newconffile); - foreach my $s (PVE::OpenVZ::SCRIPT_EXT) { - my $scriptfn = "${vmid}.$s"; - my $dstfn = "$newcfgdir/$scriptfn"; - if (-f $dstfn) { - $self->log('err', "unlink '$dstfn' failed - $!") if !unlink $dstfn; - } - } -} - -sub init_target_vm { - my ($self, $vmid) = @_; - - my $conf = $self->{vmconf}; - - $self->log('info', "initialize container on remote node '$self->{node}'"); - - my $cmd = [ @{$self->{rem_ssh}}, 'vzctl', '--quiet', 'set', $vmid, - '--applyconfig_map', 'name', '--save' ]; - - $self->cmd_quiet($cmd, errmsg => "Failed to apply config on target node"); - - if ($self->{disk_quota}) { - $self->log('info', "initializing remote quota"); - $cmd = [ @{$self->{rem_ssh}}, 'vzctl', 'quotainit', $vmid]; - $self->cmd_quiet($cmd, errmsg => "Failed to initialize quota"); - $self->log('info', "turn on remote quota"); - $cmd = [ @{$self->{rem_ssh}}, 'vzctl', 'quotaon', $vmid]; - $self->cmd_quiet($cmd, errmsg => "Failed to turn on quota"); - $self->log('info', "load 2nd level quota"); - $cmd = [ @{$self->{rem_ssh}}, "(vzdqload $vmid -U -G -T < " . - PVE::Tools::shellquote($self->{quotadumpfile}) . - " && vzquota reload2 $vmid)"]; - $self->cmd_quiet($cmd, errmsg => "Failed to load 2nd level quota"); - if (!$self->{running}) { - $self->log('info', "turn off remote quota"); - $cmd = [ @{$self->{rem_ssh}}, 'vzquota', 'off', $vmid]; - $self->cmd_quiet($cmd, errmsg => "Failed to turn off quota"); - } - } -} - -sub phase2 { - my ($self, $vmid) = @_; - - my $conf = $self->{vmconf}; - - $self->{target_initialized} = 1; - init_target_vm($self, $vmid); - - $self->log('info', "starting container on remote node '$self->{node}'"); - - $self->log('info', "restore container state"); - $self->{dumpfile} = "$self->{dumpdir}/dump.$vmid"; - my $cmd = [ @{$self->{rem_ssh}}, 'vzctl', 'restore', $vmid, '--undump', - '--dumpfile', $self->{dumpfile}, '--skip_arpdetect' ]; - $self->cmd_quiet($cmd, errmsg => "Failed to restore container"); - - $cmd = [ @{$self->{rem_ssh}}, 'vzctl', 'restore', $vmid, '--resume' ]; - $self->cmd_quiet($cmd, errmsg => "Failed to resume container"); -} - -sub phase3 { - my ($self, $vmid) = @_; - - if (!$self->{target_initialized}) { - init_target_vm($self, $vmid); - } - -} - -sub phase3_cleanup { - my ($self, $vmid, $err) = @_; - - my $conf = $self->{vmconf}; - - if (!$self->{shared}) { - # destroy local container data - $self->log('info', "removing container files on local node"); - my $cmd = [ 'rm', '-rf', $self->{privatedir} ]; - $self->cmd_logerr($cmd); - } - - if ($self->{disk_quota}) { - my $cmd = [ 'vzquota', 'drop', $vmid]; - $self->cmd_logerr($cmd, errmsg => "Failed to drop local quota"); - } -} - -sub final_cleanup { - my ($self, $vmid) = @_; - - $self->log('info', "start final cleanup"); - - my $conf = $self->{vmconf}; - - unlink($self->{quotadumpfile}) if $self->{quotadumpfile}; - - unlink($self->{dumpfile}) if $self->{dumpfile}; - - if ($self->{undo_copy_dump} && $self->{dumpfile}) { - my $cmd = [ @{$self->{rem_ssh}}, 'rm', '-f', $self->{dumpfile} ]; - $self->cmd_logerr($cmd, errmsg => "Failed to remove dump file"); - } - - if ($self->{undo_copy_quota_dump} && $self->{quotadumpfile}) { - my $cmd = [ @{$self->{rem_ssh}}, 'rm', '-f', $self->{quotadumpfile} ]; - $self->cmd_logerr($cmd, errmsg => "Failed to remove 2nd level quota dump file"); - } -} - -1; diff --git a/PVE/VZDump.pm b/PVE/VZDump.pm index f5aac84b8..c858ff021 100644 --- a/PVE/VZDump.pm +++ b/PVE/VZDump.pm @@ -13,7 +13,6 @@ use File::Path; use PVE::RPCEnvironment; use PVE::Storage; use PVE::Cluster qw(cfs_read_file); -use PVE::VZDump::OpenVZ; use Time::localtime; use Time::Local; use PVE::JSONSchema qw(get_standard_option); @@ -26,7 +25,7 @@ my $pidfile = '/var/run/vzdump.pid'; my $logdir = '/var/log/vzdump'; -my @plugins = qw (PVE::VZDump::OpenVZ); +my @plugins = qw(); # Load available plugins my $pveplug = "/usr/share/perl5/PVE/VZDump/QemuServer.pm"; @@ -850,29 +849,12 @@ sub exec_backup_task { $self->run_hook_script ('backup-start', $task, $logfd); - if ($vmtype eq 'openvz') { - # pre-suspend rsync - $plugin->copy_data_phase1 ($task, $vmid); - } - debugmsg ('info', "suspend vm", $logfd); $vmstoptime = time (); $self->run_hook_script ('pre-stop', $task, $logfd); $plugin->suspend_vm ($task, $vmid); $cleanup->{resume} = 1; - if ($vmtype eq 'openvz') { - # post-suspend rsync - $plugin->copy_data_phase2 ($task, $vmid); - - debugmsg ('info', "resume vm", $logfd); - $cleanup->{resume} = 0; - $self->run_hook_script ('pre-restart', $task, $logfd); - $plugin->resume_vm ($task, $vmid); - my $delay = time () - $vmstoptime; - debugmsg ('info', "vm is online again after $delay seconds", $logfd); - } - } elsif ($mode eq 'snapshot') { $self->run_hook_script ('backup-start', $task, $logfd); diff --git a/PVE/VZDump/Makefile b/PVE/VZDump/Makefile index 672e4e812..af1cc8f33 100644 --- a/PVE/VZDump/Makefile +++ b/PVE/VZDump/Makefile @@ -1,7 +1,6 @@ include ../../defines.mk PERLSOURCE = \ - OpenVZ.pm \ Plugin.pm all: diff --git a/PVE/VZDump/OpenVZ.pm b/PVE/VZDump/OpenVZ.pm deleted file mode 100644 index 779c3a1da..000000000 --- a/PVE/VZDump/OpenVZ.pm +++ /dev/null @@ -1,312 +0,0 @@ -package PVE::VZDump::OpenVZ; - -use strict; -use warnings; -use File::Path; -use File::Basename; -use PVE::INotify; -use PVE::VZDump; -use PVE::OpenVZ; - -use base qw (PVE::VZDump::Plugin); - -my $load_vz_conf = sub { - my ($self, $vmid) = @_; - - my $conf = PVE::OpenVZ::load_config($vmid); - - my $dir = $self->{privatedir}; - if ($conf->{ve_private} && $conf->{ve_private}->{value}) { - $dir = $conf->{ve_private}->{value}; - } - $dir =~ s/\$VEID/$vmid/; - $self->{vmlist}->{$vmid}->{dir} = $dir; - - my $hostname = "CT $vmid"; - if ($conf->{hostname} && $conf->{hostname}->{value}) { - $hostname = $conf->{hostname}->{value}; - } - $self->{vmlist}->{$vmid}->{hostname} = $hostname; -}; - -my $rsync_vm = sub { - my ($self, $task, $from, $to, $text) = @_; - - $self->loginfo ("starting $text sync $from to $to"); - - my $starttime = time(); - - my $opts = $self->{vzdump}->{opts}; - - my $rsyncopts = "--stats -x --numeric-ids"; - - $rsyncopts .= " --bwlimit=$opts->{bwlimit}" if $opts->{bwlimit}; - - $self->cmd ("rsync $rsyncopts -aH --delete --no-whole-file --inplace '$from' '$to'"); - - my $delay = time () - $starttime; - - $self->loginfo ("$text sync finished ($delay seconds)"); -}; - -sub new { - my ($class, $vzdump) = @_; - - PVE::VZDump::check_bin ('vzctl'); - - my $self = bless PVE::OpenVZ::read_global_vz_config (); - - $self->{vzdump} = $vzdump; - - $self->{vmlist} = PVE::OpenVZ::config_list(); - - return $self; -}; - -sub type { - return 'openvz'; -} - -sub vm_status { - my ($self, $vmid) = @_; - - my $status_text = ''; - $self->cmd ("vzctl status $vmid", outfunc => sub {$status_text .= shift; }); - chomp $status_text; - - my $running = $status_text =~ m/running/ ? 1 : 0; - - return wantarray ? ($running, $running ? 'running' : 'stopped') : $running; -} - -sub prepare { - my ($self, $task, $vmid, $mode) = @_; - - $self->$load_vz_conf ($vmid); - - my $dir = $self->{vmlist}->{$vmid}->{dir}; - - my $diskinfo = { dir => $dir }; - - $task->{hostname} = $self->{vmlist}->{$vmid}->{hostname}; - - $task->{diskinfo} = $diskinfo; - - my $hostname = PVE::INotify::nodename(); - - if ($mode eq 'snapshot') { - - my $lvmmap = PVE::VZDump::get_lvm_mapping(); - my ($srcdev, $lvmpath, $lvmvg, $lvmlv, $fstype) = - PVE::VZDump::get_lvm_device ($dir, $lvmmap); - - my $targetdev = PVE::VZDump::get_lvm_device ($task->{dumpdir}, $lvmmap); - - die ("mode failure - unable to detect lvm volume group\n") if !$lvmvg; - die ("mode failure - wrong lvm mount point '$lvmpath'\n") if $dir !~ m|/?$lvmpath/?|; - die ("mode failure - unable to dump into snapshot (use option --dumpdir)\n") - if $targetdev eq $srcdev; - - $diskinfo->{snapname} = "vzsnap-$hostname-0"; - $diskinfo->{snapdev} = "/dev/$lvmvg/$diskinfo->{snapname}"; - $diskinfo->{srcdev} = $srcdev; - $diskinfo->{lvmvg} = $lvmvg; - $diskinfo->{lvmlv} = $lvmlv; - $diskinfo->{fstype} = $fstype; - $diskinfo->{lvmpath} = $lvmpath; - $diskinfo->{mountpoint} = "/mnt/vzsnap0"; - - $task->{snapdir} = $dir; - $task->{snapdir} =~ s|/?$lvmpath/?|$diskinfo->{mountpoint}/|; - - } elsif ($mode eq 'suspend') { - $task->{snapdir} = $task->{tmpdir}; - } else { - $task->{snapdir} = $dir; - } -} - -sub lock_vm { - my ($self, $vmid) = @_; - - my $filename = "$self->{lockdir}/103.lck"; - - my $lockmgr = PVE::OpenVZ::create_lock_manager(); - - $self->{lock} = $lockmgr->lock($filename) || die "can't lock VM $vmid\n"; -} - -sub unlock_vm { - my ($self, $vmid) = @_; - - $self->{lock}->release(); -} - -sub copy_data_phase1 { - my ($self, $task) = @_; - - $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "first"); -} - -# we use --skiplock for vzctl because we have already locked the VM -# by calling lock_vm() - -sub stop_vm { - my ($self, $task, $vmid) = @_; - - $self->cmd ("vzctl --skiplock stop $vmid"); -} - -sub start_vm { - my ($self, $task, $vmid) = @_; - - $self->cmd ("vzctl --skiplock start $vmid"); -} - -sub suspend_vm { - my ($self, $task, $vmid) = @_; - - $self->cmd ("vzctl --skiplock chkpnt $vmid --suspend"); -} - -sub snapshot { - my ($self, $task) = @_; - - my $opts = $self->{vzdump}->{opts}; - - my $di = $task->{diskinfo}; - - mkpath $di->{mountpoint}; # create mount point for lvm snapshot - - if (-b $di->{snapdev}) { - $self->loginfo ("trying to remove stale snapshot '$di->{snapdev}'"); - - $self->cmd_noerr ("umount $di->{mountpoint}"); - - $self->cmd_noerr ("lvremove -f $di->{snapdev}"); - } - - $self->loginfo ("creating lvm snapshot of $di->{srcdev} ('$di->{snapdev}')"); - - $task->{cleanup}->{lvm_snapshot} = 1; - - $self->cmd ("lvcreate --size $opts->{size}M --snapshot" . - " --name $di->{snapname} /dev/$di->{lvmvg}/$di->{lvmlv}"); - - my $mopts = $di->{fstype} eq 'xfs' ? "-o nouuid" : ''; - - $task->{cleanup}->{snapshot_mount} = 1; - - $self->cmd ("mount -n -t $di->{fstype} $mopts $di->{snapdev} $di->{mountpoint}"); -} - -sub copy_data_phase2 { - my ($self, $task) = @_; - - $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "final"); -} - -sub resume_vm { - my ($self, $task, $vmid) = @_; - - $self->cmd ("vzctl --skiplock chkpnt $vmid --resume"); -} - -sub assemble { - my ($self, $task, $vmid) = @_; - - my $conffile = PVE::OpenVZ::config_file($vmid); - - my $dir = $task->{snapdir}; - - $task->{cleanup}->{etc_vzdump} = 1; - - mkpath "$dir/etc/vzdump/"; - $self->cmd ("cp '$conffile' '$dir/etc/vzdump/vps.conf'"); - my $cfgdir = dirname ($conffile); - foreach my $s (PVE::OpenVZ::SCRIPT_EXT) { - my $fn = "$cfgdir/$vmid.$s"; - $self->cmd ("cp '$fn' '$dir/etc/vzdump/vps.$s'") if -f $fn; - } -} - -sub archive { - my ($self, $task, $vmid, $filename, $comp) = @_; - - my $findexcl = $self->{vzdump}->{findexcl}; - my $findargs = join (' ', @$findexcl) . ' -print0'; - my $opts = $self->{vzdump}->{opts}; - - my $srcdir = $self->{vmlist}->{$vmid}->{dir}; - my $snapdir = $task->{snapdir}; - - my $taropts = "--totals --sparse --numeric-owner --no-recursion --one-file-system"; - - # note: --remove-files does not work because we do not - # backup all files (filters). tar complains: - # Cannot rmdir: Directory not empty - # we we disable this optimization for now - #if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) { - # $taropts .= " --remove-files"; # try to save space - #} - - my $cmd = "("; - - $cmd .= "cd $snapdir;find . $findargs|sed 's/\\\\/\\\\\\\\/g'|"; - $cmd .= "tar cpf - $taropts --null -T -"; - my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream - $cmd .= "|cstream -t $bwl" if $opts->{bwlimit}; - $cmd .= "|$comp" if $comp; - - $cmd .= ")"; - - if ($opts->{stdout}) { - $self->cmd ($cmd, output => ">&=" . fileno($opts->{stdout})); - } else { - $self->cmd ("$cmd >$filename"); - } -} - -sub cleanup { - my ($self, $task, $vmid) = @_; - - my $di = $task->{diskinfo}; - - if ($task->{cleanup}->{snapshot_mount}) { - # Note: sleep to avoid 'device is busy' message. - # Seems Kernel need some time to cleanup open file list, - # fir example when we stop the tar with kill (stop task) - sleep(1); - $self->cmd_noerr ("umount $di->{mountpoint}"); - } - - if ($task->{cleanup}->{lvm_snapshot}) { - # loop, because we often get 'LV in use: not deactivating' - # we use run_command() because we do not want to log errors here - my $wait = 1; - while(-b $di->{snapdev}) { - eval { - my $cmd = ['lvremove', '-f', $di->{snapdev}]; - PVE::Tools::run_command($cmd, outfunc => sub {}, errfunc => sub {}); - }; - last if !$@; - if ($wait >= 64) { - $self->logerr($@); - last; - } - $self->loginfo("lvremove failed - trying again in $wait seconds") if $wait >= 8; - sleep($wait); - $wait = $wait*2; - } - - } - - if ($task->{cleanup}->{etc_vzdump}) { - my $dir = "$task->{snapdir}/etc/vzdump"; - eval { rmtree $dir if -d $dir; }; - $self->logerr ($@) if $@; - } - -} - -1; diff --git a/bin/Makefile b/bin/Makefile index fb7ab9ed5..fb8e89aed 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -5,12 +5,10 @@ SUBDIRS = init.d cron ocf test SCRIPTS = \ pveceph \ vzdump \ - vzrestore \ pvestatd \ pvesh \ pveam \ pvebanner \ - pvectl \ pvedaemon \ pveproxy \ spiceproxy \ @@ -22,9 +20,7 @@ SCRIPTS = \ MANS = \ pveceph.1 \ - pvectl.1 \ vzdump.1 \ - vzrestore.1 \ pvestatd.1 \ pvedaemon.1 \ pveproxy.1 \ @@ -82,10 +78,8 @@ install: ${SCRIPTS} ${MANS} pvemailforward install -d ${MAN1DIR} install -m 0644 ${MANS} ${MAN1DIR} install -d ${PODDIR} - install -m 0644 pvectl.1.pod ${PODDIR} install -m 0644 vzdump.1.pod ${PODDIR} install -m 0644 pvesubscription.1.pod ${PODDIR} - install -m 0644 vzrestore.1.pod ${PODDIR} set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i $@; done .PHONY: distclean diff --git a/bin/pvectl b/bin/pvectl deleted file mode 100755 index 8f2643d95..000000000 --- a/bin/pvectl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -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; - -use Data::Dumper; # fixme: remove - -use base qw(PVE::CLIHandler); - -$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin'; - -initlog('pvectl'); - -die "please run as root\n" if $> != 0; - -PVE::INotify::inotify_init(); -my $nodename = PVE::INotify::nodename(); - -my $rpcenv = PVE::RPCEnvironment->init('cli'); - -$rpcenv->init_request(); -$rpcenv->set_language($ENV{LANG}); -$rpcenv->set_user('root@pam'); - -my $upid_exit = sub { - my $upid = shift; - my $status = PVE::Tools::upid_read_status($upid); - exit($status eq 'OK' ? 0 : -1); -}; - -my $cmddef = { - - list => [ "PVE::API2::OpenVZ", 'vmlist', [], - { node => $nodename }, sub { - my $vmlist = shift; - - exit 0 if (!scalar(@$vmlist)); - - printf "%10s %-20s %-10s %-10s %-12s\n", - qw(VMID NAME STATUS MEM(MB) DISK(GB)); - - 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); - } - } ], - - create => [ 'PVE::API2::OpenVZ', 'create_vm', ['vmid', 'ostemplate'], { node => $nodename }, $upid_exit ], - destroy => [ 'PVE::API2::OpenVZ', 'destroy_vm', ['vmid'], { node => $nodename }, $upid_exit ], - - set => [ "PVE::API2::OpenVZ", 'update_vm', ['vmid'], { node => $nodename } ], - - config => [ "PVE::API2::OpenVZ", 'vm_config', ['vmid'], - { node => $nodename }, sub { - my $config = shift; - foreach my $k (sort (keys %$config)) { - next if $k eq 'digest'; - my $v = $config->{$k}; - if ($k eq 'description') { - $v = PVE::Tools::encode_text($v); - } - print "$k: $v\n"; - } - }], - - start => [ 'PVE::API2::OpenVZ', 'vm_start', ['vmid'], { node => $nodename }, $upid_exit], - suspend => [ 'PVE::API2::OpenVZ', 'vm_suspend', ['vmid'], { node => $nodename }, $upid_exit], - resume => [ 'PVE::API2::OpenVZ', 'vm_resume', ['vmid'], { node => $nodename }, $upid_exit], - shutdown => [ 'PVE::API2::OpenVZ', 'vm_shutdown', ['vmid'], { node => $nodename }, $upid_exit], - stop => [ 'PVE::API2::OpenVZ', 'vm_stop', ['vmid'], { node => $nodename }, $upid_exit], - mount => [ 'PVE::API2::OpenVZ', 'vm_mount', ['vmid'], { node => $nodename }, $upid_exit], - umount => [ 'PVE::API2::OpenVZ', 'vm_umount', ['vmid'], { node => $nodename }, $upid_exit], - migrate => [ "PVE::API2::OpenVZ", 'migrate_vm', ['vmid', 'target'], { node => $nodename }, $upid_exit], -}; - -my $cmd = shift; - -PVE::CLIHandler::handle_cmd($cmddef, "pvectl", $cmd, \@ARGV, undef, $0); - -exit 0; - -__END__ - -=head1 NAME - -pvectl - vzctl wrapper to manage OpenVZ containers - -=head1 SYNOPSIS - -=include synopsis - -=head1 DESCRIPTION - -This is a small wrapper around vztl. - -=include pve_copyright diff --git a/bin/pvestatd b/bin/pvestatd index 05109150b..2ba17f9df 100755 --- a/bin/pvestatd +++ b/bin/pvestatd @@ -13,7 +13,6 @@ use PVE::INotify; use PVE::Cluster qw(cfs_read_file); use PVE::Storage; use PVE::QemuServer; -use PVE::OpenVZ; use PVE::RPCEnvironment; use PVE::API2::Subscription; use PVE::AutoBalloon; @@ -202,45 +201,6 @@ sub find_vzctl_console_pids { return $res; } -sub remove_stale_openvz_consoles { - - my $vmstatus = PVE::OpenVZ::vmstatus(); - my $pidhash = find_vzctl_console_pids(); - - foreach my $vmid (keys %$pidhash) { - next if defined($vmstatus->{$vmid}); - syslog('info', "remove stale vzctl console for CT $vmid"); - foreach my $pid (@{$pidhash->{$vmid}}) { - kill(9, $pid); - } - } -} - -sub update_openvz_status { - - my $ctime = time(); - - my $vmstatus = PVE::OpenVZ::vmstatus(); - - foreach my $vmid (keys %$vmstatus) { - my $d = $vmstatus->{$vmid}; - my $data; - if ($d->{status} eq 'running') { # running - $data = "$d->{uptime}:$d->{name}:$d->{status}:0:$ctime:$d->{cpus}:$d->{cpu}:" . - "$d->{maxmem}:$d->{mem}:" . - "$d->{maxdisk}:$d->{disk}:" . - "$d->{netin}:$d->{netout}:" . - "$d->{diskread}:$d->{diskwrite}"; - } else { - $data = "0:$d->{name}:$d->{status}:0:$ctime:$d->{cpus}::" . - "$d->{maxmem}::" . - "$d->{maxdisk}:$d->{disk}:" . - ":::"; - } - PVE::Cluster::broadcast_rrd("pve2.3-vm/$vmid", $data); - } -} - sub update_storage_status { my $cfg = cfs_read_file("storage.cfg"); @@ -287,23 +247,11 @@ sub update_status { $err = $@; syslog('err', "qemu status update error: $err") if $err; - eval { - update_openvz_status(); - }; - $err = $@; - syslog('err', "openvz status update error: $err") if $err; - eval { update_storage_status(); }; $err = $@; syslog('err', "storage status update error: $err") if $err; - - eval { - remove_stale_openvz_consoles(); - }; - $err = $@; - syslog('err', "openvz console cleanup error: $err") if $err; } my $next_update = 0; diff --git a/bin/vzrestore b/bin/vzrestore deleted file mode 100755 index ba85f2bc9..000000000 --- a/bin/vzrestore +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/perl -w - -use strict; -use PVE::SafeSyslog; -use PVE::Tools qw(extract_param); -use PVE::INotify; -use PVE::RPCEnvironment; -use PVE::CLIHandler; -use PVE::JSONSchema qw(get_standard_option); -use PVE::API2::OpenVZ; - -use Data::Dumper; # fixme: remove - -use base qw(PVE::CLIHandler); - -$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin'; - -initlog('vzrestore'); - -die "please run as root\n" if $> != 0; - -PVE::INotify::inotify_init(); - -my $rpcenv = PVE::RPCEnvironment->init('cli'); - -$rpcenv->init_request(); -$rpcenv->set_language($ENV{LANG}); -$rpcenv->set_user('root@pam'); - -__PACKAGE__->register_method({ - name => 'vzrestore', - path => 'vzrestore', - method => 'POST', - description => "Restore OpenVZ containers.", - parameters => { - additionalProperties => 0, - properties => { - vmid => get_standard_option('pve-vmid'), - archive => { - description => "The backup file. You can pass '-' to read from standard input.", - type => 'string', - maxLength => 255, - }, - storage => get_standard_option('pve-storage-id', { - description => "Target storage.", - default => 'local', - optional => 1, - }), - force => { - optional => 1, - type => 'boolean', - description => "Allow to overwrite existing container.", - }, - }, - }, - returns => { - type => 'string', - }, - code => sub { - my ($param) = @_; - - $param->{ostemplate} = extract_param($param, 'archive'); - - $param->{node} = PVE::INotify::nodename(); - - $param->{restore} = 1; - - return PVE::API2::OpenVZ->create_vm($param); - }}); - -my $cmddef = [ __PACKAGE__, 'vzrestore', ['archive', 'vmid'], undef, - sub { - my $upid = shift; - my $status = PVE::Tools::upid_read_status($upid); - exit($status eq 'OK' ? 0 : -1); - }]; - -push @ARGV, 'help' if !scalar(@ARGV); - -PVE::CLIHandler::handle_simple_cmd($cmddef, \@ARGV, undef, $0); - -exit 0; - -__END__ - -=head1 NAME - -vzrestore - restore OpenVZ vzdump backups - -=head1 SYNOPSIS - -=include synopsis - -=head1 DESCRIPTION - -Restores OpenVZ vzdump backups. - -=head1 SEE ALSO - -vzdump(1) qmrestore(1) - -=include pve_copyright