]> git.proxmox.com Git - pve-guest-common.git/blobdiff - PVE/ReplicationConfig.pm
fix #1291: implement remove_vmid_from_backup_jobs
[pve-guest-common.git] / PVE / ReplicationConfig.pm
index a15b599a1f58c552675493a48912ce62c41e52d9..e58597ea6dd153c48ba259f77789bf75b2a7ede3 100644 (file)
@@ -20,10 +20,29 @@ cfs_register_file($replication_cfg_filename,
                  sub { __PACKAGE__->parse_config(@_); },
                  sub { __PACKAGE__->write_config(@_); });
 
+PVE::JSONSchema::register_format('pve-replication-job-id',
+                                \&parse_replication_job_id);
+sub parse_replication_job_id {
+    my ($id, $noerr) = @_;
+
+    my $msg = "invalid replication job id '$id'";
+
+    if ($id =~ m/^(\d+)-(\d+)$/) {
+       my ($guest, $jobnum) = (int($1), int($2));
+       die "$msg (guest IDs < 100 are reseved)\n" if $guest < 100;
+       my $parsed_id = "$guest-$jobnum"; # use parsed integers
+       return wantarray ? ($guest, $jobnum, $parsed_id) :  $parsed_id;
+    }
+
+    return undef if $noerr;
+
+    die "$msg\n";
+}
+
 PVE::JSONSchema::register_standard_option('pve-replication-id', {
-    description => "Replication Job ID.",
-    type => 'string', format => 'pve-configid',
-    maxLength => 32, # keep short to reduce snapshot name length
+    description => "Replication Job ID. The ID is composed of a Guest ID and a job number, separated by a hyphen, i.e. '<GUEST>-<JOBNUM>'.",
+    type => 'string', format => 'pve-replication-job-id',
+    pattern => '[1-9][0-9]{2,8}-\d{1,9}',
 });
 
 my $defaultData = {
@@ -41,9 +60,12 @@ my $defaultData = {
            optional => 1,
            maxLength => 4096,
        },
-       guest => get_standard_option('pve-vmid', {
+       remove_job => {
+           description => "Mark the replication job for removal. The job will remove all local replication snapshots. When set to 'full', it also tries to remove replicated volumes on the target. The job then removes itself from the configuration file.",
+           type => 'string',
+           enum => ['local', 'full'],
            optional => 1,
-           completion => \&PVE::Cluster::complete_vmid }),
+       },
        rate => {
            description => "Rate limit in mbps (megabytes per second) as floating point number.",
            type => 'number',
@@ -57,6 +79,11 @@ my $defaultData = {
            default => '*/15',
            optional => 1,
        },
+       source => {
+           description => "Source of the replication.",
+           type => 'string', format => 'pve-node',
+           optional => 1,
+       },
     },
 };
 
@@ -67,10 +94,11 @@ sub private {
 sub parse_section_header {
     my ($class, $line) = @_;
 
-    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
-       my ($type, $id) = (lc($1), $2);
+    if ($line =~ m/^(\S+):\s*(\d+)-(\d+)\s*$/) {
+       my ($type, $guest, $subid) = (lc($1), int($2), int($3));
+       my $id = "$guest-$subid"; # use parsed integers
        my $errmsg = undef; # set if you want to skip whole section
-       eval { PVE::JSONSchema::pve_verify_configid($id); };
+       eval { parse_replication_job_id($id); };
        $errmsg = $@ if $@;
        my $config = {};
        return ($type, $id, $errmsg, $config);
@@ -97,6 +125,12 @@ sub parse_config {
     foreach my $id (sort keys %{$cfg->{ids}}) {
        my $data = $cfg->{ids}->{$id};
 
+       my ($guest, $jobnum) = parse_replication_job_id($id);
+
+       $data->{guest} = $guest;
+       $data->{jobnum} = $jobnum;
+       $data->{id} = $id;
+
        $data->{comment} = PVE::Tools::decode_text($data->{comment})
            if defined($data->{comment});
 
@@ -127,6 +161,7 @@ sub write_config {
        my $tid = $plugin->get_unique_target_id($data);
        my $vmid = $data->{guest};
 
+       die "property 'guest' has wrong value\n" if $id !~ m/^\Q$vmid\E-/;
        die "replication job for guest '$vmid' to target '$tid' already exists\n"
            if defined($target_hash->{$vmid}->{$tid});
        $target_hash->{$vmid}->{$tid} = 1;
@@ -135,7 +170,7 @@ sub write_config {
            if defined($data->{comment});
     }
 
-    $class->SUPER::write_config($filename, $cfg);
+    return $class->SUPER::write_config($filename, $cfg);
 }
 
 sub new {
@@ -164,6 +199,91 @@ sub lock {
     }
 }
 
+sub check_for_existing_jobs {
+    my ($cfg, $vmid, $noerr) = @_;
+
+    foreach my $id (keys %{$cfg->{ids}}) {
+       my $data = $cfg->{ids}->{$id};
+
+       if ($data->{guest} == $vmid) {
+           return 1 if $noerr;
+           die "There is a replication job '$id' for guest '$vmid' - " .
+               "Please remove that first.\n"
+       }
+    }
+
+    return undef;
+}
+
+sub find_local_replication_job {
+    my ($cfg, $vmid, $target) = @_;
+
+    foreach my $id (keys %{$cfg->{ids}}) {
+       my $data = $cfg->{ids}->{$id};
+
+       return $data if $data->{type} eq 'local' &&
+           $data->{guest} == $vmid && $data->{target} eq $target;
+    }
+
+    return undef;
+}
+
+# switch local replication job target
+sub switch_replication_job_target {
+    my ($vmid, $old_target, $new_target) = @_;
+
+    my $transfer_job = sub {
+       my $cfg = PVE::ReplicationConfig->new();
+       my $jobcfg = find_local_replication_job($cfg, $vmid, $old_target);
+
+       return if !$jobcfg;
+
+       $jobcfg->{target} = $new_target;
+
+       $cfg->write();
+    };
+
+    lock($transfer_job);
+};
+
+sub delete_job {
+    my ($jobid) = @_;
+
+    my $code = sub {
+       my $cfg = __PACKAGE__->new();
+       delete $cfg->{ids}->{$jobid};
+       $cfg->write();
+    };
+
+    lock($code);
+}
+
+sub remove_vmid_jobs {
+    my ($vmid) = @_;
+
+    my $code = sub {
+       my $cfg = __PACKAGE__->new();
+       foreach my $id (keys %{$cfg->{ids}}) {
+           delete $cfg->{ids}->{$id} if ($cfg->{ids}->{$id}->{guest} == $vmid);
+       }
+       $cfg->write();
+    };
+
+    lock($code);
+}
+
+sub swap_source_target_nolock {
+    my ($jobid) = @_;
+
+    my $cfg = __PACKAGE__->new();
+    my $job = $cfg->{ids}->{$jobid};
+    my $tmp = $job->{source};
+    $job->{source} = $job->{target};
+    $job->{target} = $tmp;
+    $cfg->write();
+
+    return $cfg->{ids}->{$jobid};
+}
 
 package PVE::ReplicationConfig::Cluster;
 
@@ -184,12 +304,13 @@ sub properties {
 
 sub options {
     return {
-       guest => { fixed => 1, optional => 0 },
        target => { fixed => 1, optional => 0 },
        disable => { optional => 1 },
        comment => { optional => 1 },
        rate => { optional => 1 },
        schedule => { optional => 1 },
+       remove_job => { optional => 1 },
+       source => { optional => 1 },
     };
 }