]>
git.proxmox.com Git - pve-cluster.git/blob - data/PVE/pvecm
10 use Data
::Dumper
; # fixme: remove
17 use base
qw(PVE::CLIHandler);
19 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
21 $ENV{HOME
} = '/root'; # for ssh-copy-id
23 die "please run as root\n" if $> != 0;
25 my $nodename = PVE
::INotify
::nodename
();
26 # trigger check that we have resolvable name
27 my $local_ip_address = PVE
::Cluster
::remote_node_ip
($nodename);
29 my $basedir = "/etc/pve";
30 my $clusterconf = "$basedir/corosync.conf";
31 my $libdir = "/var/lib/pve-cluster";
32 my $backupdir = "/var/lib/pve-cluster/backup";
33 my $dbfile = "$libdir/config.db";
34 my $authfile = "/etc/corosync/authkey";
39 print "backup old database\n";
44 my $cmd = "echo '.dump' |";
45 $cmd .= "sqlite3 '$dbfile' |";
46 $cmd .= "gzip - >'${backupdir}/config-${ctime}.sql.gz'";
49 die "can't backup old database: $!\n";
55 foreach my $fn (<$backupdir/config-*.sql
.gz
>) {
56 if ($fn =~ m!/config-(\d+)\.sql.gz$!) {
57 push @bklist, [$fn, $1];
61 @bklist = sort { $b->[1] <=> $a->[1] } @bklist;
63 while (scalar (@bklist) >= $maxfiles) {
65 print "delete old backup '$d->[0]'\n";
70 __PACKAGE__-
>register_method ({
74 description
=> "Generate new cryptographic key for corosync.",
76 additionalProperties
=> 0,
80 description
=> "Output file name"
84 returns
=> { type
=> 'null' },
89 my $filename = $param->{filename
};
92 $> == 0 || die "Error: Authorization key must be generated as root user.\n";
93 my $dirname = dirname
($filename);
94 my $basename = basename
($filename);
96 die "key file '$filename' already exists\n" if -e
$filename;
98 File
::Path
::make_path
($dirname) if $dirname;
100 my $cmd = ['corosync-keygen', '-l', '-k', $filename];
101 PVE
::Tools
::run_command
($cmd);
106 __PACKAGE__-
>register_method ({
110 description
=> "Generate new cluster configuration.",
112 additionalProperties
=> 0,
115 description
=> "The name of the cluster.",
116 type
=> 'string', format
=> 'pve-node',
121 description
=> "Node id for this node.",
127 description
=> "Number of votes for this node.",
133 returns
=> { type
=> 'null' },
138 -f
$clusterconf && die "cluster config '$clusterconf' already exists\n";
140 PVE
::Cluster
::setup_rootsshconfig
();
141 PVE
::Cluster
::setup_ssh_keys
();
143 -f
$authfile || __PACKAGE__-
>keygen({filename
=> $authfile});
145 -f
$authfile || die "no authentication key available\n";
147 my $clustername = $param->{clustername
};
149 $param->{nodeid
} = 1 if !$param->{nodeid
};
151 $param->{votes
} = 1 if !defined($param->{votes
});
157 cluster_name: $clustername
163 ring0_addr: $nodename
164 nodeid: $param->{nodeid}
165 quorum_votes: $param->{votes}
170 provider: corosync_votequorum
179 PVE
::Tools
::file_set_contents
($clusterconf, $config);
181 PVE
::Cluster
::ssh_merge_keys
();
183 PVE
::Cluster
::gen_pve_node_files
($nodename, $local_ip_address);
185 PVE
::Cluster
::ssh_merge_known_hosts
($nodename, $local_ip_address, 1);
187 PVE
::Tools
::run_command
('service pve-cluster restart'); # restart
189 PVE
::Tools
::run_command
('service corosync restart'); # restart
194 __PACKAGE__-
>register_method ({
198 description
=> "Adds a node to the cluster configuration.",
200 additionalProperties
=> 0,
202 node
=> PVE
::JSONSchema
::get_standard_option
('pve-node'),
205 description
=> "Node id for this node.",
211 description
=> "Number of votes for this node",
217 description
=> "Do not throw error if node already exists.",
222 returns
=> { type
=> 'null' },
227 PVE
::Cluster
::check_cfs_quorum
();
231 my $name = $param->{node
};
233 if (defined(my $res = $lst->{$name})) {
234 $param->{nodeid
} = $res->{nodeid
} if !$param->{nodeid
};
235 $param->{votes
} = $res->{votes
} if !defined($param->{votes
});
237 if ($res->{votes
} == $param->{votes
} &&
238 $res->{nodeid
} == $param->{nodeid
}) {
239 print "node $name already defined\n";
240 if ($param->{force
}) {
246 } elsif (!$param->{nodeid
}) {
251 foreach my $v (values %$lst) {
252 if ($v->{nodeid
} eq $nodeid) {
261 $param->{nodeid
} = $nodeid;
264 $param->{votes
} = 1 if !defined($param->{votes
});
266 PVE
::Cluster
::gen_local_dirs
($name);
268 eval { PVE
::Cluster
::ssh_merge_keys
(); };
271 my $cmd = ['ccs_tool', 'addnode', '-c', $clusterconf];
273 # NOTE: cman does not like votes="0"
274 if ($param->{votes
}) {
275 push @$cmd, '-v', $param->{votes
};
278 push @$cmd, '-n', $param->{nodeid
}, $name;
280 system(@$cmd) == 0 || exit(-1);
286 __PACKAGE__-
>register_method ({
290 description
=> "Removes a node to the cluster configuration.",
292 additionalProperties
=> 0,
294 node
=> PVE
::JSONSchema
::get_standard_option
('pve-node'),
297 returns
=> { type
=> 'null' },
302 PVE
::Cluster
::check_cfs_quorum
();
304 my $cmd = ['ccs_tool', 'delnode', '-c', $clusterconf, $param->{node
}];
308 exit (-1); # should not be reached
311 __PACKAGE__-
>register_method ({
315 description
=> "Adds the current node to an existing cluster.",
317 additionalProperties
=> 0,
321 description
=> "Hostname (or IP) of an existing cluster member."
325 description
=> "Node id for this node.",
331 description
=> "Number of votes for this node",
337 description
=> "Do not throw error if node already exists.",
342 returns
=> { type
=> 'null' },
347 PVE
::Cluster
::setup_rootsshconfig
();
348 PVE
::Cluster
::setup_ssh_keys
();
350 my $host = $param->{hostname
};
352 if (!$param->{force
}) {
355 die "authentication key already exists\n";
358 if (-f
$clusterconf) {
359 die "cluster config '$clusterconf' already exists\n";
362 my $vmlist = PVE
::Cluster
::get_vmlist
();
363 if ($vmlist && $vmlist->{ids
} && scalar(keys %{$vmlist->{ids
}})) {
364 die "this host already contains virtual machines - please remove the first\n";
367 if (system("corosync-quorumtool >/dev/null 2>&1") == 0) {
368 die "corosync is already running\n";
372 # make sure known_hosts is on local filesystem
373 PVE
::Cluster
::ssh_unmerge_known_hosts
();
375 my $cmd = "ssh-copy-id -i /root/.ssh/id_rsa 'root\@$host' >/dev/null 2>&1";
376 system ($cmd) == 0 ||
377 die "unable to copy ssh ID\n";
379 $cmd = ['ssh', $host, '-o', 'BatchMode=yes',
380 'pvecm', 'addnode', $nodename, '--force', 1];
382 push @$cmd, '--nodeid', $param->{nodeid
} if $param->{nodeid
};
384 push @$cmd, '--votes', $param->{votes
} if defined($param->{votes
});
386 if (system (@$cmd) != 0) {
387 my $cmdtxt = join (' ', @$cmd);
388 die "unable to add node: command failed ($cmdtxt)\n";
391 my $tmpdir = "$libdir/.pvecm_add.tmp.$$";
395 print "copy corosync auth key\n";
396 $cmd = ['rsync', '--rsh=ssh -l root -o BatchMode=yes', '-lpgoq',
397 "$host:$authfile $clusterconf", $tmpdir];
399 system(@$cmd) == 0 || die "can't rsync data from host '$host'\n";
401 mkdir "/etc/cluster";
402 my $confbase = basename
($clusterconf);
404 $cmd = "cp '$tmpdir/$confbase' '/etc/cluster/$confbase'";
405 system($cmd) == 0 || die "can't copy cluster configuration\n";
407 my $keybase = basename
($authfile);
408 system ("cp '$tmpdir/$keybase' '$authfile'") == 0 ||
409 die "can't copy '$tmpdir/$keybase' to '$authfile'\n";
411 print "stopping pve-cluster service\n";
413 system("umount $basedir -f >/dev/null 2>&1");
414 system("/etc/init.d/pve-cluster stop") == 0 ||
415 die "can't stop pve-cluster service\n";
421 system("/etc/init.d/pve-cluster start") == 0 ||
422 die "starting pve-cluster failed\n";
424 system("/etc/init.d/cman start");
428 while (!PVE
::Cluster
::check_cfs_quorum
(1)) {
430 print "waiting for quorum...";
436 print "OK\n" if !$printqmsg;
438 system("/etc/init.d/clvm start");
440 print "generating node certificates\n";
441 PVE
::Cluster
::gen_pve_node_files
($nodename, $local_ip_address);
443 print "merge known_hosts file\n";
444 PVE
::Cluster
::ssh_merge_known_hosts
($nodename, $local_ip_address, 1);
446 print "restart services\n";
447 # restart pvedaemon (changed certs)
448 system("/etc/init.d/pvedaemon restart");
449 # restart pveproxy (changed certs)
450 system("/etc/init.d/pveproxy restart");
452 print "successfully added node '$nodename' to cluster.\n";
463 __PACKAGE__-
>register_method ({
467 description
=> "Displays the local view of the cluster status.",
469 additionalProperties
=> 0,
472 returns
=> { type
=> 'null' },
477 my $cmd = ['corosync-quorumtool', '-siH'];
481 exit (-1); # should not be reached
484 __PACKAGE__-
>register_method ({
488 description
=> "Displays the local view of the cluster nodes.",
490 additionalProperties
=> 0,
493 returns
=> { type
=> 'null' },
498 my $cmd = ['corosync-quorumtool', '-l'];
502 exit (-1); # should not be reached
505 __PACKAGE__-
>register_method ({
509 description
=> "Tells CMAN a new value of expected votes.",
511 additionalProperties
=> 0,
515 description
=> "Expected votes",
520 returns
=> { type
=> 'null' },
525 my $cmd = ['cman_tool', 'expected', '-e', $param->{expected
}];
529 exit (-1); # should not be reached
537 my $cmd = ['ccs_tool', 'lsnode', '-c', $clusterconf];
543 if ($line =~ m/^Nodename\s+Votes\s+Nodeid\s/i) {
548 if ($line =~ m/^(\S+)\s+(\d+)\s+(\d+)\s/) {
556 PVE
::Tools
::run_command
($cmd, outfunc
=> $parser);
560 __PACKAGE__-
>register_method ({
561 name
=> 'updatecerts',
562 path
=> 'updatecerts',
564 description
=> "Update node certificates (and generate all needed files/directories).",
566 additionalProperties
=> 0,
569 description
=> "Force generation of new SSL certifate.",
574 description
=> "Ignore errors (i.e. when cluster has no quorum).",
580 returns
=> { type
=> 'null' },
584 PVE
::Cluster
::setup_rootsshconfig
();
586 PVE
::Cluster
::gen_pve_vzdump_symlink
();
588 if (!PVE
::Cluster
::check_cfs_quorum
(1)) {
589 return undef if $param->{silent
};
590 die "no quorum - unable to update files\n";
593 PVE
::Cluster
::setup_ssh_keys
();
595 PVE
::Cluster
::gen_pve_node_files
($nodename, $local_ip_address, $param->{force
});
596 PVE
::Cluster
::ssh_merge_keys
();
597 PVE
::Cluster
::ssh_merge_known_hosts
($nodename, $local_ip_address);
598 PVE
::Cluster
::gen_pve_vzdump_files
();
605 keygen
=> [ __PACKAGE__
, 'keygen', ['filename']],
606 create
=> [ __PACKAGE__
, 'create', ['clustername']],
607 add
=> [ __PACKAGE__
, 'add', ['hostname']],
608 addnode
=> [ __PACKAGE__
, 'addnode', ['node']],
609 delnode
=> [ __PACKAGE__
, 'delnode', ['node']],
610 status
=> [ __PACKAGE__
, 'status' ],
611 nodes
=> [ __PACKAGE__
, 'nodes' ],
612 expected
=> [ __PACKAGE__
, 'expected', ['expected']],
613 updatecerts
=> [ __PACKAGE__
, 'updatecerts', []],
618 if ($cmd && $cmd ne 'printmanpod' && $cmd ne 'verifyapi') {
619 PVE
::Cluster
::check_cfs_is_mounted
();
620 PVE
::Cluster
::cfs_update
();
623 PVE
::CLIHandler
::handle_cmd
($cmddef, "pvecm", $cmd, \
@ARGV, undef, $0);
631 pvecm - Proxmox VE cluster manager toolkit
639 pvecm is a program to manage the cluster configuration. It can be used
640 to create a new cluster, join nodes to a cluster, leave the cluster,
641 get status information and do various other cluster related tasks.
643 =include pve_copyright