Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MrPowerGamerBR/27fad27129c6a8a6a4bcb7d1c6a11a84 to your computer and use it in GitHub Desktop.
Save MrPowerGamerBR/27fad27129c6a8a6a4bcb7d1c6a11a84 to your computer and use it in GitHub Desktop.
Patch Proxmox to allow cloning of bind mounts

This is a workaround, I don't really like but it works

To allow cloning templates with bind mounts (Useful if you have folders that are shared, so you know that the folder will be accessible in clones), open /usr/share/perl5/PVE/API2/LXC.pm and find this part of the code (in my current version, it is line 1413)

		foreach my $opt (keys %$src_conf) {
		    next if $opt =~ m/^unused\d+$/;

		    my $value = $src_conf->{$opt};

		    if (($opt eq 'rootfs') || ($opt =~ m/^mp\d+$/)) {
			my $mp = PVE::LXC::Config->parse_volume($opt, $value);

			if ($mp->{type} eq 'volume') {
			    my $volid = $mp->{volume};

			    my ($sid, $volname) = PVE::Storage::parse_volume_id($volid);
			    $sid = $storage if defined($storage);
			    my $scfg = PVE::Storage::storage_config($storecfg, $sid);
			    if (!$scfg->{shared}) {
				$sharedvm = 0;
				warn "found non-shared volume: $volid\n" if $target;
			    }

			    $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);

			    if ($full) {
				die "Cannot do full clones on a running container without snapshots\n"
				    if $running && !defined($snapname);
				$fullclone->{$opt} = 1;
			    } else {
				# not full means clone instead of copy
				die "Linked clone feature for '$volid' is not available\n"
				    if !PVE::Storage::volume_has_feature($storecfg, 'clone', $volid, $snapname, $running, {'valid_target_formats' => ['raw', 'subvol']});
			    }

			    $mountpoints->{$opt} = $mp;
			    push @$vollist, $volid;

			} else {
			    # TODO: allow bind mounts?
			    die "unable to clone mountpint '$opt' (type $mp->{type})\n";
			}
		    } elsif ($opt =~ m/^net(\d+)$/) {
			# always change MAC! address
			my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
			my $net = PVE::LXC::Config->parse_lxc_network($value);
			$net->{hwaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
			$newconf->{$opt} = PVE::LXC::Config->print_lxc_network($net);
		    } else {
			# copy everything else
			$newconf->{$opt} = $value;
		    }
		}

After finding this part of the code, go to the # TODO: allow bind mounts? section and add this check:

if ($mp->{type} eq 'bind') {
	$newconf->{$opt} = $value;
} else {
	# TODO: allow bind mounts?
	die "unable to clone mountpint '$opt' (type $mp->{type})\n";
}

This will copy the bind mount as is! Keep in mind that this may cause issues when you clone LXC with bind mounts that should not be cloned in a different LXC container.

End result:

		foreach my $opt (keys %$src_conf) {
		    next if $opt =~ m/^unused\d+$/;

		    my $value = $src_conf->{$opt};

		    if (($opt eq 'rootfs') || ($opt =~ m/^mp\d+$/)) {
			my $mp = PVE::LXC::Config->parse_volume($opt, $value);

			if ($mp->{type} eq 'volume') {
			    my $volid = $mp->{volume};

			    my ($sid, $volname) = PVE::Storage::parse_volume_id($volid);
			    $sid = $storage if defined($storage);
			    my $scfg = PVE::Storage::storage_config($storecfg, $sid);
			    if (!$scfg->{shared}) {
				$sharedvm = 0;
				warn "found non-shared volume: $volid\n" if $target;
			    }

			    $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);

			    if ($full) {
				die "Cannot do full clones on a running container without snapshots\n"
				    if $running && !defined($snapname);
				$fullclone->{$opt} = 1;
			    } else {
				# not full means clone instead of copy
				die "Linked clone feature for '$volid' is not available\n"
				    if !PVE::Storage::volume_has_feature($storecfg, 'clone', $volid, $snapname, $running, {'valid_target_formats' => ['raw', 'subvol']});
			    }

			    $mountpoints->{$opt} = $mp;
			    push @$vollist, $volid;

			} else {
				if ($mp->{type} eq 'bind') {
					$newconf->{$opt} = $value;
				} else {
					# TODO: allow bind mounts?
					die "unable to clone mountpint '$opt' (type $mp->{type})\n";
				}
			}
		    } elsif ($opt =~ m/^net(\d+)$/) {
			# always change MAC! address
			my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
			my $net = PVE::LXC::Config->parse_lxc_network($value);
			$net->{hwaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
			$newconf->{$opt} = PVE::LXC::Config->print_lxc_network($net);
		    } else {
			# copy everything else
			$newconf->{$opt} = $value;
		    }
		}

And that's it! This doesn't fix container cloning via the the dashboard but it does work via the command line interface. Tested with pve-manager/6.2-15/48bd51b6 (running kernel: 5.4.65-1-pve)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment