]>
git.proxmox.com Git - pve-cluster.git/blob - data/PVE/pvecm
5a7074fda7b5ccb99458922e111ba76dc62ad4db
8 use Data
::Dumper
; # fixme: remove
15 use base
qw(PVE::CLIHandler);
17 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
19 $ENV{HOME
} = '/root'; # for ssh-copy-id
21 die "please run as root\n" if $> != 0;
23 my $nodename = PVE
::INotify
::nodename
();
24 # trigger check that we have resolvable name
25 my $local_ip_address = PVE
::Cluster
::remote_node_ip
($nodename);
27 my $basedir = "/etc/pve";
28 my $clusterconf = "$basedir/cluster.conf";
29 my $libdir = "/var/lib/pve-cluster";
30 my $backupdir = "/var/lib/pve-cluster/backup";
31 my $dbfile = "$libdir/config.db";
32 my $authfile = "$libdir/corosync.authkey";
37 print "backup old database\n";
42 my $cmd = "echo '.dump' |";
43 $cmd .= "sqlite3 '$dbfile' |";
44 $cmd .= "gzip - >'${backupdir}/config-${ctime}.sql.gz'";
47 die "can't backup old database: $!\n";
53 foreach my $fn (<$backupdir/config-*.sql
.gz
>) {
54 if ($fn =~ m!/config-(\d+)\.sql.gz$!) {
55 push @bklist, [$fn, $1];
59 @bklist = sort { $b->[1] <=> $a->[1] } @bklist;
61 while (scalar (@bklist) >= $maxfiles) {
63 print "delete old backup '$d->[0]'\n";
68 __PACKAGE__-
>register_method ({
72 description
=> "Generate new cryptographic key for corosync.",
74 additionalProperties
=> 0,
78 description
=> "Output file name"
82 returns
=> { type
=> 'null' },
87 my $filename = $param->{filename
};
90 $> == 0 || die "Error: Authorization key must be generated as root user.\n";
91 my $dirname = dirname
($filename);
92 my $basename = basename
($filename);
94 File
::Path
::make_path
($dirname) if $dirname;
96 my $fh = IO
::File-
>new ("/dev/urandom", 'r') ||
97 die "can't open /dev/urandom - $!\n";
103 while ($bytes < $keysize) {
104 my $rb = sysread($fh, $key, $keysize - $bytes, $bytes);
105 ($rb > 0) || die "Could not read /dev/urandom - $!\n";
111 my $tmpfn = "$filename.tmp.$$";
112 $fh = IO
::File-
>new ($tmpfn, O_CREAT
|O_WRONLY
, 0400);
113 die "can't open temporary file '$tmpfn' - $!\n" if !$fh;
117 (($wb = syswrite($fh, $key)) == $keysize) ||
118 die "writing key failed - short write $wb\n;"
129 if (!rename($tmpfn, $filename)) {
132 die "rename $tmpfn to $filename failed - $err\n";
138 __PACKAGE__-
>register_method ({
142 description
=> "Generate new cluster configuration.",
144 additionalProperties
=> 0,
147 description
=> "The name of the cluster.",
148 type
=> 'string', format
=> 'pve-node',
153 description
=> "Node id for this node.",
159 description
=> "Number of votes for this node",
165 returns
=> { type
=> 'null' },
170 -f
$clusterconf && die "cluster config '$clusterconf' already exists\n";
172 PVE
::Cluster
::setup_rootsshconfig
();
173 PVE
::Cluster
::setup_ssh_keys
();
175 -f
$authfile || __PACKAGE__-
>keygen({filename
=> $authfile});
177 -f
$authfile || die "no authentication key available\n";
179 my $clustername = $param->{clustername
};
181 $param->{nodeid
} = 1 if !$param->{nodeid
};
183 $param->{votes
} = 1 if !defined($param->{votes
});
186 <?xml version="1.0"?>
187 <cluster name="$clustername" config_version="1">
189 <cman keyfile="$authfile">
193 <clusternode name="${nodename}" votes="$param->{votes}" nodeid="$param->{nodeid}"/>
199 PVE
::Tools
::file_set_contents
($clusterconf, $config);
201 PVE
::Cluster
::ssh_merge_keys
();
203 PVE
::Cluster
::gen_pve_node_files
($nodename, $local_ip_address);
205 PVE
::Cluster
::ssh_merge_known_hosts
($nodename, $local_ip_address, 1);
207 PVE
::Tools
::run_command
('/etc/init.d/pve-cluster restart'); # restart
209 # that cman init script returns strange values - simply ignore for now
210 system('/etc/init.d/cman start');
213 system('/etc/init.d/clvm start');
218 __PACKAGE__-
>register_method ({
222 description
=> "Adds a node to the cluster configuration.",
224 additionalProperties
=> 0,
226 node
=> PVE
::JSONSchema
::get_standard_option
('pve-node'),
229 description
=> "Node id for this node.",
235 description
=> "Number of votes for this node",
241 description
=> "Do not throw error if node already exists.",
246 returns
=> { type
=> 'null' },
251 PVE
::Cluster
::check_cfs_quorum
();
255 my $name = $param->{node
};
257 if (defined(my $res = $lst->{$name})) {
258 $param->{nodeid
} = $res->{nodeid
} if !$param->{nodeid
};
259 $param->{votes
} = $res->{votes
} if !defined($param->{votes
});
261 if ($res->{votes
} == $param->{votes
} &&
262 $res->{nodeid
} == $param->{nodeid
}) {
263 print "node $name already defined\n";
264 if ($param->{force
}) {
270 } elsif (!$param->{nodeid
}) {
275 foreach my $v (values %$lst) {
276 if ($v->{nodeid
} eq $nodeid) {
285 $param->{nodeid
} = $nodeid;
288 $param->{votes
} = 1 if !defined($param->{votes
});
290 PVE
::Cluster
::gen_local_dirs
($name);
292 eval { PVE
::Cluster
::ssh_merge_keys
(); };
295 my $cmd = ['ccs_tool', 'addnode', '-c', $clusterconf];
297 # NOTE: cman does not like votes="0"
298 if ($param->{votes
}) {
299 push @$cmd, '-v', $param->{votes
};
302 push @$cmd, '-n', $param->{nodeid
}, $name;
304 system(@$cmd) == 0 || exit(-1);
310 __PACKAGE__-
>register_method ({
314 description
=> "Removes a node to the cluster configuration.",
316 additionalProperties
=> 0,
318 node
=> PVE
::JSONSchema
::get_standard_option
('pve-node'),
321 returns
=> { type
=> 'null' },
326 PVE
::Cluster
::check_cfs_quorum
();
328 my $cmd = ['ccs_tool', 'delnode', '-c', $clusterconf, $param->{node
}];
332 exit (-1); # should not be reached
335 __PACKAGE__-
>register_method ({
339 description
=> "Adds the current node to an existing cluster.",
341 additionalProperties
=> 0,
345 description
=> "Hostname (or IP) of an existing cluster member."
349 description
=> "Node id for this node.",
355 description
=> "Number of votes for this node",
361 description
=> "Do not throw error if node already exists.",
366 returns
=> { type
=> 'null' },
371 PVE
::Cluster
::setup_rootsshconfig
();
372 PVE
::Cluster
::setup_ssh_keys
();
374 my $host = $param->{hostname
};
376 if (!$param->{force
}) {
379 die "authentication key already exists\n";
382 if (-f
$clusterconf) {
383 die "cluster config '$clusterconf' already exists\n";
386 my $vmlist = PVE
::Cluster
::get_vmlist
();
387 if ($vmlist && $vmlist->{ids
} && scalar(keys %{$vmlist->{ids
}})) {
388 die "this host already contains virtual machines - please remove the first\n";
391 if (system("cman_tool status >/dev/null 2>&1") == 0) {
392 die "cman is already running\n";
396 # make sure known_hosts is on local filesystem
397 PVE
::Cluster
::ssh_unmerge_known_hosts
();
399 my $cmd = "ssh-copy-id -i /root/.ssh/id_rsa 'root\@$host' >/dev/null 2>&1";
400 system ($cmd) == 0 ||
401 die "unable to copy ssh ID\n";
403 $cmd = ['ssh', $host, '-o', 'BatchMode=yes',
404 'pvecm', 'addnode', $nodename, '--force', 1];
406 push @$cmd, '--nodeid', $param->{nodeid
} if $param->{nodeid
};
408 push @$cmd, '--votes', $param->{votes
} if defined($param->{votes
});
410 if (system (@$cmd) != 0) {
411 my $cmdtxt = join (' ', @$cmd);
412 die "unable to add node: command failed ($cmdtxt)\n";
415 my $tmpdir = "$libdir/.pvecm_add.tmp.$$";
419 print "copy corosync auth key\n";
420 $cmd = ['rsync', '--rsh=ssh -l root -o BatchMode=yes', '-lpgoq',
421 "$host:$authfile $clusterconf", $tmpdir];
423 system(@$cmd) == 0 || die "can't rsync data from host '$host'\n";
425 mkdir "/etc/cluster";
426 my $confbase = basename
($clusterconf);
428 $cmd = "cp '$tmpdir/$confbase' '/etc/cluster/$confbase'";
429 system($cmd) == 0 || die "can't copy cluster configuration\n";
431 my $keybase = basename
($authfile);
432 system ("cp '$tmpdir/$keybase' '$authfile'") == 0 ||
433 die "can't copy '$tmpdir/$keybase' to '$authfile'\n";
435 print "stopping pve-cluster service\n";
437 system("umount $basedir -f >/dev/null 2>&1");
438 system("/etc/init.d/pve-cluster stop") == 0 ||
439 die "can't stop pve-cluster service\n";
445 system("/etc/init.d/pve-cluster start") == 0 ||
446 die "starting pve-cluster failed\n";
448 system("/etc/init.d/cman start");
452 while (!PVE
::Cluster
::check_cfs_quorum
(1)) {
454 print "waiting for quorum...";
460 print "OK\n" if !$printqmsg;
462 system("/etc/init.d/clvm start");
464 print "generating node certificates\n";
465 PVE
::Cluster
::gen_pve_node_files
($nodename, $local_ip_address);
467 print "merge known_hosts file\n";
468 PVE
::Cluster
::ssh_merge_known_hosts
($nodename, $local_ip_address, 1);
470 print "restart services\n";
471 # restart pvedaemon (changed certs)
472 system("/etc/init.d/pvedaemon restart");
473 # restart pveproxy (changed certs)
474 system("/etc/init.d/pveproxy restart");
476 print "successfully added node '$nodename' to cluster.\n";
487 __PACKAGE__-
>register_method ({
491 description
=> "Displays the local view of the cluster status.",
493 additionalProperties
=> 0,
496 returns
=> { type
=> 'null' },
501 my $cmd = ['cman_tool', 'status'];
505 exit (-1); # should not be reached
508 __PACKAGE__-
>register_method ({
512 description
=> "Displays the local view of the cluster nodes.",
514 additionalProperties
=> 0,
517 returns
=> { type
=> 'null' },
522 my $cmd = ['cman_tool', 'nodes'];
526 exit (-1); # should not be reached
529 __PACKAGE__-
>register_method ({
533 description
=> "Tells CMAN a new value of expected votes.",
535 additionalProperties
=> 0,
539 description
=> "Expected votes",
544 returns
=> { type
=> 'null' },
549 my $cmd = ['cman_tool', 'expected', '-e', $param->{expected
}];
553 exit (-1); # should not be reached
561 my $cmd = ['ccs_tool', 'lsnode', '-c', $clusterconf];
567 if ($line =~ m/^Nodename\s+Votes\s+Nodeid\s/i) {
572 if ($line =~ m/^(\S+)\s+(\d+)\s+(\d+)\s/) {
580 PVE
::Tools
::run_command
($cmd, outfunc
=> $parser);
584 __PACKAGE__-
>register_method ({
585 name
=> 'updatecerts',
586 path
=> 'updatecerts',
588 description
=> "Update node certificates (and generate all needed files/directories).",
590 additionalProperties
=> 0,
593 description
=> "Force generation of new SSL certifate.",
598 description
=> "Ignore errors (i.e. when cluster has no quorum).",
604 returns
=> { type
=> 'null' },
608 PVE
::Cluster
::setup_rootsshconfig
();
610 PVE
::Cluster
::gen_pve_vzdump_symlink
();
612 if (!PVE
::Cluster
::check_cfs_quorum
(1)) {
613 return undef if $param->{silent
};
614 die "no quorum - unable to update files\n";
617 PVE
::Cluster
::setup_ssh_keys
();
619 PVE
::Cluster
::gen_pve_node_files
($nodename, $local_ip_address, $param->{force
});
620 PVE
::Cluster
::ssh_merge_keys
();
621 PVE
::Cluster
::ssh_merge_known_hosts
($nodename, $local_ip_address);
622 PVE
::Cluster
::gen_pve_vzdump_files
();
629 keygen
=> [ __PACKAGE__
, 'keygen', ['filename']],
630 create
=> [ __PACKAGE__
, 'create', ['clustername']],
631 add
=> [ __PACKAGE__
, 'add', ['hostname']],
632 addnode
=> [ __PACKAGE__
, 'addnode', ['node']],
633 delnode
=> [ __PACKAGE__
, 'delnode', ['node']],
634 status
=> [ __PACKAGE__
, 'status' ],
635 nodes
=> [ __PACKAGE__
, 'nodes' ],
636 expected
=> [ __PACKAGE__
, 'expected', ['expected']],
637 updatecerts
=> [ __PACKAGE__
, 'updatecerts', []],
642 if ($cmd && $cmd ne 'printmanpod' && $cmd ne 'verifyapi') {
643 PVE
::Cluster
::check_cfs_is_mounted
();
644 PVE
::Cluster
::cfs_update
();
647 PVE
::CLIHandler
::handle_cmd
($cmddef, "pvecm", $cmd, \
@ARGV, undef, $0);
655 pvecm - Proxmox VE cluster manager toolkit
663 pvecm is a program to manage the cluster configuration. It can be used
664 to create a new cluster, join nodes to a cluster, leave the cluster,
665 get status information and do various other cluster related tasks.
667 =include pve_copyright