ostype => {
optional => 1,
type => 'string',
- enum => ['debian', 'ubuntu', 'centos'],
+ enum => ['debian', 'ubuntu', 'centos', 'archlinux'],
description => "OS type. Corresponds to lxc setup scripts in /usr/share/lxc/config/<ostype>.common.conf.",
},
console => {
next;
}
- if ($line =~ m/^(lxc\.[a-z0-9\.]+)(:|\s*=)\s*(.*?)\s*$/) {
+ if ($line =~ m/^(lxc\.[a-z0-9_\.]+)(:|\s*=)\s*(.*?)\s*$/) {
my $key = $1;
my $value = $3;
if ($valid_lxc_conf_keys->{$key} || $key =~ m/^lxc\.cgroup\./) {
sub lock_filename {
my ($vmid) = @_;
- return "$lockdir/pve-config-{$vmid}.lock";
+ return "$lockdir/pve-config-${vmid}.lock";
}
sub lock_aquire {
die "can't aquire lock - $!\n";
}
- $lock_handles->{$$}->{$filename}->{refcount}++;
-
print STDERR " OK\n";
}
+
+ $lock_handles->{$$}->{$filename}->{refcount}++;
};
eval { PVE::Tools::run_with_timeout($timeout, $lock_func); };
}
}
- return undef if !$res->{volume};
+ return undef if !defined($res->{volume});
return undef if $res->{backup} && $res->{backup} !~ m/^(yes|no)$/;
$raw .= "lxc.cgroup.cpu.shares = $shares\n";
my $mountpoint = parse_ct_mountpoint($conf->{rootfs});
+ $mountpoint->{mp} = '/';
my $volid = $mountpoint->{volume};
- my $path = volid_path ($volid, $storage_cfg);
+ my $path = mountpoint_mount_path($mountpoint, $storage_cfg);
+
my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
if ($storage) {
sub destroy_lxc_container {
my ($storage_cfg, $vmid, $conf) = @_;
- my $rootinfo = parse_ct_mountpoint($conf->{rootfs});
- if (defined($rootinfo->{volume})) {
- my ($vtype, $name, $owner) = PVE::Storage::parse_volname($storage_cfg, $rootinfo->{volume});
- PVE::Storage::vdisk_free($storage_cfg, $rootinfo->{volume}) if $vmid == $owner;;
- }
+ foreach_mountpoint($conf, sub {
+ my ($ms, $mountpoint) = @_;
+ my ($vtype, $name, $owner) = PVE::Storage::parse_volname($storage_cfg, $mountpoint->{volume});
+ PVE::Storage::vdisk_free($storage_cfg, $mountpoint->{volume}) if $vmid == $owner;
+ });
+
rmdir "/var/lib/lxc/$vmid/rootfs";
unlink "/var/lib/lxc/$vmid/config";
rmdir "/var/lib/lxc/$vmid";
my $vollist = get_vm_volumes($conf);
my $loopdevlist = get_vm_volumes($conf, 'rootfs');
- dettach_loops($storage_cfg, $loopdevlist);
+ detach_loops($storage_cfg, $loopdevlist);
PVE::Storage::deactivate_volumes($storage_cfg, $vollist);
}
};
return 1 if defined $conf->{template} && $conf->{template} == 1;
}
-sub foreach_mountpoint {
- my ($conf, $func) = @_;
+sub mountpoint_names {
+ my ($reverse) = @_;
- my $mountpoint = parse_ct_mountpoint($conf->{rootfs});
- $mountpoint->{mp} = '/'; # just to be sure
- &$func('rootfs', $mountpoint);
+ my @names = ('rootfs');
for (my $i = 0; $i < $MAX_MOUNT_POINTS; $i++) {
- my $key = "mp$i";
- next if !defined($conf->{$key});
- $mountpoint = parse_ct_mountpoint($conf->{$key});
+ push @names, "mp$i";
+ }
+
+ return $reverse ? reverse @names : @names;
+}
+
+sub foreach_mountpoint_full {
+ my ($conf, $reverse, $func) = @_;
+
+ foreach my $key (mountpoint_names($reverse)) {
+ my $value = $conf->{$key};
+ next if !defined($value);
+ my $mountpoint = parse_ct_mountpoint($value);
+ $mountpoint->{mp} = '/' if $key eq 'rootfs'; # just to be sure
&$func($key, $mountpoint);
}
}
+sub foreach_mountpoint {
+ my ($conf, $func) = @_;
+
+ foreach_mountpoint_full($conf, 0, $func);
+}
+
+sub foreach_mountpoint_reverse {
+ my ($conf, $func) = @_;
+
+ foreach_mountpoint_full($conf, 1, $func);
+}
+
sub loopdevices_list {
my $loopdev = {};
return 1;
}
-
-sub volid_path {
- my ($volid, $storage_cfg, $loopdevs) = @_;
-
- my $path;
-
- my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
-
- if ($storage) {
-
- my $scfg = PVE::Storage::storage_config($storage_cfg, $storage);
- $path = PVE::Storage::path($storage_cfg, $volid);
-
- my ($vtype, undef, undef, undef, undef, $isBase, $format) =
- PVE::Storage::parse_volname($storage_cfg, $volid);
-
- die "unable to use template as mountpoint\n" if $isBase;
-
- if ($format eq 'subvol') {
- # do nothing
- } elsif ($format eq 'raw') {
-
- if ($scfg->{path}) {
- $path = find_loopdev($loopdevs, $path) if $loopdevs;
- } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'rbd') {
- # do nothing
- } else {
- die "unsupported storage type '$scfg->{type}'\n";
- }
- } else {
- die "unsupported image format '$format'\n";
- }
- } elsif ($volid =~ m|^/dev/.+|) {
- $path = $volid;
- } elsif ($volid !~ m|^/dev/.+| && $volid =~ m|^/.+| && -d $volid) {
- $path = $volid;
- } else {
- die "unsupported storage";
- }
-
- return $path;
-}
-
sub attach_loops {
- my ($storage_cfg, $vollist) = @_;
+ my ($storage_cfg, $vollist, $snapname) = @_;
my $loopdevs = {};
my ($vtype, undef, undef, undef, undef, $isBase, $format) =
PVE::Storage::parse_volname($storage_cfg, $volid);
- if (($format ne 'subvol') &&
- ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs')) {
- my $path = PVE::Storage::path($storage_cfg, $volid);
+ if ($format eq 'raw' && $scfg->{path}) {
+ my $path = PVE::Storage::path($storage_cfg, $volid, $snapname);
my $loopdev;
my $parser = sub {
return $loopdevs;
}
-sub dettach_loops {
- my ($storage_cfg, $vollist) = @_;
+sub detach_loops {
+ my ($storage_cfg, $vollist, $snapname) = @_;
my $loopdevs = loopdevices_list();
my ($vtype, undef, undef, undef, undef, $isBase, $format) =
PVE::Storage::parse_volname($storage_cfg, $volid);
- if($format ne 'subvol' && ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs')) {
- my $path = PVE::Storage::path($storage_cfg, $volid);
+ if ($format eq 'raw' && $scfg->{path}) {
+ my $path = PVE::Storage::path($storage_cfg, $volid, $snapname);
foreach my $dev (keys %$loopdevs){
PVE::Tools::run_command(['losetup', '-d', $dev]) if $loopdevs->{$dev} eq $path;
}
}
}
+sub umount_all {
+ my ($vmid, $storage_cfg, $conf, $noerr, $loopdevs) = @_;
+
+ $loopdevs ||= loopdevices_list();
+
+ my $rootdir = "/var/lib/lxc/$vmid/rootfs";
+ my $volid_list = get_vm_volumes($conf);
+
+ foreach_mountpoint_reverse($conf, sub {
+ my ($ms, $mountpoint) = @_;
+
+ my $volid = $mountpoint->{volume};
+ my $mount = $mountpoint->{mp};
+
+ return if !$volid || !$mount;
+
+ my $mount_path = "$rootdir/$mount";
+ $mount_path =~ s!/+!/!g;
+
+ # fixme: test if mounted?
+ eval {
+ PVE::Tools::run_command(['umount', '-d', $mount_path]);
+ };
+ if (my $err = $@) {
+ if ($noerr) {
+ warn $err;
+ } else {
+ die $err;
+ }
+ }
+ });
+
+ PVE::LXC::detach_loops($storage_cfg, $volid_list);
+}
+
+sub mount_all {
+ my ($vmid, $storage_cfg, $conf) = @_;
+
+ my $rootdir = "/var/lib/lxc/$vmid/rootfs";
+
+ my $volid_list = get_vm_volumes($conf);
+ PVE::Storage::activate_volumes($storage_cfg, $volid_list);
+
+ my $loopdevs;
+ eval {
+ $loopdevs = attach_loops($storage_cfg, $volid_list);
+
+ foreach_mountpoint($conf, sub {
+ my ($ms, $mountpoint) = @_;
+
+ my $volid = $mountpoint->{volume};
+ my $mount = $mountpoint->{mp};
+
+ return if !$volid || !$mount;
+
+ my $image_path = PVE::Storage::path($storage_cfg, $volid);
+ my ($vtype, undef, undef, undef, undef, $isBase, $format) =
+ PVE::Storage::parse_volname($storage_cfg, $volid);
+
+ die "unable to mount base volume - internal error" if $isBase;
+
+ mountpoint_mount($mountpoint, $rootdir, $storage_cfg, $loopdevs);
+ });
+ };
+ if (my $err = $@) {
+ warn "mounting container failed - $err";
+ umount_all($vmid, $storage_cfg, $conf, 1);
+ }
+
+ return ($rootdir, $loopdevs) if wantarray;
+ return $rootdir;
+}
+
+
+sub mountpoint_mount_path {
+ my ($mountpoint, $storage_cfg, $loopdevs, $snapname) = @_;
+
+ return mountpoint_mount($mountpoint, undef, $storage_cfg, $loopdevs, $snapname);
+}
+# use $rootdir = undef to just return the corresponding mount path
sub mountpoint_mount {
- my ($mountpoint, $rootdir, $storage_cfg, $loopdevs) = @_;
+ my ($mountpoint, $rootdir, $storage_cfg, $loopdevs, $snapname) = @_;
my $volid = $mountpoint->{volume};
my $mount = $mountpoint->{mp};
-
+
return if !$volid || !$mount;
- $rootdir =~ s!/+$!!;
- my $mount_path = "$rootdir/$mount";
- File::Path::mkpath($mount_path);
-
- if ($volid =~ m|^/dev/.+|) {
- PVE::Tools::run_command(['mount', $volid, $mount_path]);
- return;
+ my $mount_path;
+
+ if (defined($rootdir)) {
+ $rootdir =~ s!/+$!!;
+ $mount_path = "$rootdir/$mount";
+ $mount_path =~ s!/+!/!g;
+ File::Path::mkpath($mount_path);
}
+
+ my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
- my $path = volid_path($volid, $storage_cfg, $loopdevs);
+ die "unknown snapshot path for '$volid'" if !$storage && defined($snapname);
- if ($path !~ m|^/dev/.+|) {
- PVE::Tools::run_command(['mount', '-o', 'bind', $path, $mount_path]);
- return;
- }
+ if ($storage) {
- PVE::Tools::run_command(['mount', $path, $mount_path]);
+ my $scfg = PVE::Storage::storage_config($storage_cfg, $storage);
+ my $path = PVE::Storage::path($storage_cfg, $volid, $snapname);
+
+ my ($vtype, undef, undef, undef, undef, $isBase, $format) =
+ PVE::Storage::parse_volname($storage_cfg, $volid);
+
+ if ($format eq 'subvol') {
+
+ if ($mount_path) {
+ if ($snapname) {
+ if ($scfg->{type} eq 'zfspool') {
+ my $path_arg = $path;
+ $path_arg =~ s!^/+!!;
+ PVE::Tools::run_command(['mount', '-o', 'ro', '-t', 'zfs', $path_arg, $mount_path]);
+ } else {
+ die "cannot mount subvol snapshots for storage type '$scfg->{type}'\n";
+ }
+ } else {
+ PVE::Tools::run_command(['mount', '-o', 'bind', $path, $mount_path]);
+ }
+ }
+ return $path;
+
+ } elsif ($format eq 'raw') {
+
+ if ($scfg->{path}) {
+ $path = find_loopdev($loopdevs, $path) if $loopdevs;
+ } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'rbd') {
+ # do nothing
+ } else {
+ die "unsupported storage type '$scfg->{type}'\n";
+ }
+ if ($mount_path) {
+ if ($isBase || defined($snapname)) {
+ PVE::Tools::run_command(['mount', '-o', 'ro', $path, $mount_path]);
+ } else {
+ PVE::Tools::run_command(['mount', $path, $mount_path]);
+ }
+ }
+ return $path;
+ } else {
+ die "unsupported image format '$format'\n";
+ }
+ } elsif ($volid =~ m|^/dev/.+|) {
+ PVE::Tools::run_command(['mount', $volid, $mount_path]) if $mount_path;
+ return $volid;
+ } elsif ($volid !~ m|^/dev/.+| && $volid =~ m|^/.+| && -d $volid) {
+ PVE::Tools::run_command(['mount', '-o', 'bind', $volid, $mount_path]) if $mount_path;
+ return $volid;
+ }
+
+ die "unsupported storage";
}
sub get_vm_volumes {
return $vollist;
}
+# bash completion helper
+
+sub complete_os_templates {
+ my ($cmdname, $pname, $cvalue) = @_;
+
+ my $cfg = PVE::Storage::config();
+
+ my $storeid;
+
+ if ($cvalue =~ m/^([^:]+):/) {
+ $storeid = $1;
+ }
+
+ my $vtype = $cmdname eq 'restore' ? 'backup' : 'vztmpl';
+ my $data = PVE::Storage::template_list($cfg, $storeid, $vtype);
+
+ my $res = [];
+ foreach my $id (keys %$data) {
+ foreach my $item (@{$data->{$id}}) {
+ push @$res, $item->{volid} if defined($item->{volid});
+ }
+ }
+
+ return $res;
+}
+
+sub complete_migration_target {
+
+ my $res = [];
+
+ my $nodelist = PVE::Cluster::get_nodelist();
+ foreach my $node (@$nodelist) {
+ next if $node eq $nodename;
+ push @$res, $node;
+ }
+
+ return $res;
+}
+
+my $complete_ctid_full = sub {
+ my ($running) = @_;
+
+ my $idlist = vmstatus();
+
+ my $active_hash = list_active_containers();
+
+ my $res = [];
+
+ foreach my $id (keys %$idlist) {
+ my $d = $idlist->{$id};
+ if (defined($running)) {
+ next if $d->{template};
+ next if $running && !$active_hash->{$id};
+ next if !$running && $active_hash->{$id};
+ }
+ push @$res, $id;
+
+ }
+ return $res;
+};
+
+sub complete_ctid {
+ return &$complete_ctid_full();
+}
+
+sub complete_ctid_stopped {
+ return &$complete_ctid_full(0);
+}
+
+sub complete_ctid_running {
+ return &$complete_ctid_full(1);
+}
+
1;