use Net::IP;
use File::Path;
use File::Basename;
-use Data::Dumper; # fixme: remove
+use Data::Dumper; # fixme: remove
use PVE::Tools;
use PVE::Cluster;
use PVE::INotify;
print "backup old database\n";
mkdir $backupdir;
-
+
my $ctime = time();
my $cmd = [
['echo', '.dump'],
['sqlite3', $dbfile],
- ['gzip', '-', \">${backupdir}/config-${ctime}.sql.gz"],
+ ['gzip', '-', \ ">${backupdir}/config-${ctime}.sql.gz"],
];
- PVE::Tools::run_command($cmd, 'errmsg' => "can't backup old database\n");
+ PVE::Tools::run_command($cmd, 'errmsg' => "cannot backup old database\n");
# purge older backup
my $maxfiles = 10;
push @bklist, [$fn, $1];
}
}
-
+
@bklist = sort { $b->[1] <=> $a->[1] } @bklist;
while (scalar (@bklist) >= $maxfiles) {
}
__PACKAGE__->register_method ({
- name => 'keygen',
+ name => 'keygen',
path => 'keygen',
method => 'PUT',
description => "Generate new cryptographic key for corosync.",
},
},
returns => { type => 'null' },
-
+
code => sub {
my ($param) = @_;
File::Path::make_path($dirname) if $dirname;
my $cmd = ['corosync-keygen', '-l', '-k', $filename];
- PVE::Tools::run_command($cmd);
+ PVE::Tools::run_command($cmd);
return undef;
}});
__PACKAGE__->register_method ({
- name => 'create',
+ name => 'create',
path => 'create',
method => 'PUT',
description => "Generate new cluster configuration.",
},
},
returns => { type => 'null' },
-
+
code => sub {
my ($param) = @_;
-f $clusterconf && die "cluster config '$clusterconf' already exists\n";
- PVE::Cluster::setup_sshd_config();
+ PVE::Cluster::setup_sshd_config(1);
PVE::Cluster::setup_rootsshconfig();
PVE::Cluster::setup_ssh_keys();
$param->{votes} = 1 if !defined($param->{votes});
my $nodename = PVE::INotify::nodename();
-
+
my $local_ip_address = PVE::Cluster::remote_node_ip($nodename);
$param->{bindnet0_addr} = $local_ip_address
PVE::Tools::run_command('systemctl restart pve-cluster'); # restart
PVE::Tools::run_command('systemctl restart corosync'); # restart
-
+
return undef;
}});
__PACKAGE__->register_method ({
- name => 'addnode',
+ name => 'addnode',
path => 'addnode',
method => 'PUT',
description => "Adds a node to the cluster configuration.",
},
},
returns => { type => 'null' },
-
+
code => sub {
my ($param) = @_;
my $conf = PVE::Cluster::cfs_read_file("corosync.conf");
- my $nodelist = corosync_nodelist($conf);
+ my $nodelist = PVE::Cluster::corosync_nodelist($conf);
- my $totem_cfg = corosync_totem_config($conf);
+ my $totem_cfg = PVE::Cluster::corosync_totem_config($conf);
my $name = $param->{node};
}
} elsif (!$param->{nodeid}) {
my $nodeid = 1;
-
+
while(1) {
- my $found = 0;
+ my $found = 0;
foreach my $v (values %$nodelist) {
if ($v->{nodeid} eq $nodeid) {
$found = 1;
};
$nodelist->{$name}->{ring1_addr} = $param->{ring1_addr} if $param->{ring1_addr};
$nodelist->{$name}->{quorum_votes} = $param->{votes} if $param->{votes};
-
- corosync_update_nodelist($conf, $nodelist);
-
+
+ PVE::Cluster::corosync_update_nodelist($conf, $nodelist);
+
exit (0);
}});
__PACKAGE__->register_method ({
- name => 'delnode',
+ name => 'delnode',
path => 'delnode',
method => 'PUT',
description => "Removes a node to the cluster configuration.",
},
},
returns => { type => 'null' },
-
+
code => sub {
my ($param) = @_;
my $conf = PVE::Cluster::cfs_read_file("corosync.conf");
- my $nodelist = corosync_nodelist($conf);
+ my $nodelist = PVE::Cluster::corosync_nodelist($conf);
my $node;
my $nodeid;
delete $nodelist->{$node};
- corosync_update_nodelist($conf, $nodelist);
+ PVE::Cluster::corosync_update_nodelist($conf, $nodelist);
PVE::Tools::run_command(['corosync-cfgtool','-k', $nodeid])
if defined($nodeid);
}});
__PACKAGE__->register_method ({
- name => 'add',
+ name => 'add',
path => 'add',
method => 'PUT',
description => "Adds the current node to an existing cluster.",
},
},
returns => { type => 'null' },
-
+
code => sub {
my ($param) = @_;
my $nodename = PVE::INotify::nodename();
- PVE::Cluster::setup_sshd_config();
+ PVE::Cluster::setup_sshd_config(1);
PVE::Cluster::setup_rootsshconfig();
PVE::Cluster::setup_ssh_keys();
my $host = $param->{hostname};
if (!$param->{force}) {
-
+
if (-f $authfile) {
die "authentication key already exists\n";
}
eval {
print "copy corosync auth key\n";
- $cmd = ['rsync', '--rsh=ssh -l root -o BatchMode=yes', '-lpgoq',
+ $cmd = ['rsync', '--rsh=ssh -l root -o BatchMode=yes', '-lpgoq',
"[$host]:$authfile $clusterconf", $tmpdir];
system(@$cmd) == 0 || die "can't rsync data from host '$host'\n";
my $local_ip_address = PVE::Cluster::remote_node_ip($nodename);
print "generating node certificates\n";
- PVE::Cluster::gen_pve_node_files($nodename, $local_ip_address);
+ PVE::Cluster::gen_pve_node_files($nodename, $local_ip_address);
print "merge known_hosts file\n";
PVE::Cluster::ssh_merge_known_hosts($nodename, $local_ip_address, 1);
}});
__PACKAGE__->register_method ({
- name => 'status',
+ name => 'status',
path => 'status',
method => 'GET',
description => "Displays the local view of the cluster status.",
properties => {},
},
returns => { type => 'null' },
-
+
code => sub {
my ($param) = @_;
}});
__PACKAGE__->register_method ({
- name => 'nodes',
+ name => 'nodes',
path => 'nodes',
method => 'GET',
description => "Displays the local view of the cluster nodes.",
properties => {},
},
returns => { type => 'null' },
-
+
code => sub {
my ($param) = @_;
}});
__PACKAGE__->register_method ({
- name => 'expected',
+ name => 'expected',
path => 'expected',
method => 'PUT',
description => "Tells corosync a new value of expected votes.",
},
},
returns => { type => 'null' },
-
+
code => sub {
my ($param) = @_;
}});
-sub corosync_update_nodelist {
- my ($conf, $nodelist) = @_;
-
- delete $conf->{digest};
-
- my $version = PVE::Cluster::corosync_conf_version($conf);
- PVE::Cluster::corosync_conf_version($conf, undef, $version + 1);
-
- my $children = [];
- foreach my $v (values %$nodelist) {
- next if !($v->{ring0_addr} || $v->{name});
- my $kv = [];
- foreach my $k (keys %$v) {
- push @$kv, { key => $k, value => $v->{$k} };
- }
- my $ns = { section => 'node', children => $kv };
- push @$children, $ns;
- }
-
- foreach my $main (@{$conf->{children}}) {
- next if !defined($main->{section});
- if ($main->{section} eq 'nodelist') {
- $main->{children} = $children;
- last;
- }
- }
-
-
- PVE::Cluster::cfs_write_file("corosync.conf.new", $conf);
-
- rename("/etc/pve/corosync.conf.new", "/etc/pve/corosync.conf")
- || die "activate corosync.conf.new failed - $!\n";
-}
-
-sub corosync_nodelist {
- my ($conf) = @_;
-
- my $nodelist = {};
-
- foreach my $main (@{$conf->{children}}) {
- next if !defined($main->{section});
- if ($main->{section} eq 'nodelist') {
- foreach my $ne (@{$main->{children}}) {
- next if !defined($ne->{section}) || ($ne->{section} ne 'node');
- my $node = { quorum_votes => 1 };
- my $name;
- foreach my $child (@{$ne->{children}}) {
- next if !defined($child->{key});
- $node->{$child->{key}} = $child->{value};
- # use 'name' over 'ring0_addr' if set
- if ($child->{key} eq 'name') {
- delete $nodelist->{$name} if $name;
- $name = $child->{value};
- $nodelist->{$name} = $node;
- } elsif(!$name && $child->{key} eq 'ring0_addr') {
- $name = $child->{value};
- $nodelist->{$name} = $node;
- }
- }
- }
- }
- }
-
- return $nodelist;
-}
-
-# get a hash representation of the corosync config totem section
-sub corosync_totem_config {
- my ($conf) = @_;
-
- my $res = {};
-
- foreach my $main (@{$conf->{children}}) {
- next if !defined($main->{section}) ||
- $main->{section} ne 'totem';
-
- foreach my $e (@{$main->{children}}) {
-
- if ($e->{section} && $e->{section} eq 'interface') {
- my $entry = {};
-
- $res->{interface} = {};
-
- foreach my $child (@{$e->{children}}) {
- next if !defined($child->{key});
- $entry->{$child->{key}} = $child->{value};
- if($child->{key} eq 'ringnumber') {
- $res->{interface}->{$child->{value}} = $entry;
- }
- }
-
- } elsif ($e->{key}) {
- $res->{$e->{key}} = $e->{value};
- }
- }
- }
-
- return $res;
-}
-
__PACKAGE__->register_method ({
- name => 'updatecerts',
+ name => 'updatecerts',
path => 'updatecerts',
method => 'PUT',
description => "Update node certificates (and generate all needed files/directories).",
code => sub {
my ($param) = @_;
+ PVE::Cluster::setup_sshd_config(0);
PVE::Cluster::setup_rootsshconfig();
PVE::Cluster::gen_pve_vzdump_symlink();
return undef;
}});
+__PACKAGE__->register_method ({
+ name => 'mtunnel',
+ path => 'mtunnel',
+ method => 'POST',
+ description => "Used by VM/CT migration - do not use manually.",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ get_migration_ip => {
+ type => 'boolean',
+ default => 0,
+ description => 'return the migration IP, if configured',
+ optional => 1,
+ },
+ migration_network => {
+ type => 'string',
+ format => 'CIDR',
+ description => 'the migration network used to detect the local migration IP',
+ optional => 1,
+ },
+ },
+ },
+ returns => { type => 'null'},
+ code => sub {
+ my ($param) = @_;
+
+ if (!PVE::Cluster::check_cfs_quorum(1)) {
+ print "no quorum\n";
+ return undef;
+ }
+
+ if ($param->{get_migration_ip}) {
+ my $network = $param->{migration_network};
+ if (my $ip = PVE::Cluster::get_local_migration_ip($network)) {
+ print "ip: '$ip'\n";
+ } else {
+ print "no ip\n";
+ }
+ # do not keep tunnel open when asked for migration ip
+ return undef;
+ }
+
+ print "tunnel online\n";
+ *STDOUT->flush();
+
+ while (my $line = <>) {
+ chomp $line;
+ last if $line =~ m/^quit$/;
+ }
+
+ return undef;
+ }});
+
our $cmddef = {
keygen => [ __PACKAGE__, 'keygen', ['filename']],
nodes => [ __PACKAGE__, 'nodes' ],
expected => [ __PACKAGE__, 'expected', ['expected']],
updatecerts => [ __PACKAGE__, 'updatecerts', []],
+ mtunnel => [ __PACKAGE__, 'mtunnel', []],
};
1;