]> git.proxmox.com Git - pmg-api.git/blame - PMG/CLI/pmgcm.pm
fix typo
[pmg-api.git] / PMG / CLI / pmgcm.pm
CommitLineData
cba17aeb
DM
1package PMG::CLI::pmgcm;
2
3use strict;
4use warnings;
5use Data::Dumper;
e16a9efc 6use Term::ReadLine;
ba11e2d3 7use POSIX qw(strftime);
e16a9efc 8use JSON;
cba17aeb
DM
9
10use PVE::SafeSyslog;
11use PVE::Tools qw(extract_param);
12use PVE::INotify;
13use PVE::CLIHandler;
14
8162c745 15use PMG::Utils;
ba11e2d3 16use PMG::Ticket;
cfdf6608 17use PMG::RESTEnvironment;
cba17aeb 18use PMG::DBTools;
9d369141
DM
19use PMG::RuleDB;
20use PMG::RuleCache;
cba17aeb
DM
21use PMG::Cluster;
22use PMG::ClusterConfig;
fb5f2d1e 23use PMG::API2::Cluster;
cba17aeb
DM
24
25use base qw(PVE::CLIHandler);
26
cfdf6608
DM
27sub setup_environment {
28 PMG::RESTEnvironment->setup_default_cli_env();
ba11e2d3
DM
29
30 my $rpcenv = PMG::RESTEnvironment->get();
31 # API /config/cluster/nodes need a ticket to connect to other nodes
32 my $ticket = PMG::Ticket::assemble_ticket('root@pam');
33 $rpcenv->set_ticket($ticket);
cfdf6608
DM
34}
35
36my $upid_exit = sub {
37 my $upid = shift;
38 my $status = PVE::Tools::upid_read_status($upid);
39 exit($status eq 'OK' ? 0 : -1);
40};
41
cba17aeb
DM
42my $format_nodelist = sub {
43 my $res = shift;
44
3862c23d
DM
45 if (!scalar(@$res)) {
46 print "no cluster defined\n";
47 return;
48 }
49
cba17aeb
DM
50 print "NAME(CID)--------------IPADDRESS----ROLE-STATE---------UPTIME---LOAD----MEM---DISK\n";
51 foreach my $ni (@$res) {
ba11e2d3
DM
52 my $state = 'A';
53 $state = 'S' if !$ni->{insync};
54
55 if (my $err = $ni->{conn_error}) {
56 $err =~ s/\n/ /g;
57 $state = "ERROR: $err";
58 }
59
8162c745 60 my $uptime = $ni->{uptime} ? PMG::Utils::format_uptime($ni->{uptime}) : '-';
ba11e2d3
DM
61
62 my $loadavg1 = '-';
63 if (my $d = $ni->{loadavg}) {
64 $loadavg1 = $d->[0];
65 }
66
67 my $mem = '-';
68 if (my $d = $ni->{memory}) {
69 $mem = int(0.5 + ($d->{used}*100/$d->{total}));
70 }
71 my $disk = '-';
72 if (my $d = $ni->{rootfs}) {
d6de75ed 73 $disk = int(0.5 + ($d->{used}*100/$d->{total}));
ba11e2d3 74 }
3879a8aa
DM
75
76 printf "%-20s %-15s %-6s %1s %15s %6s %5s%% %5s%%\n",
77 "$ni->{name}($ni->{cid})", $ni->{ip}, $ni->{type},
ba11e2d3 78 $state, $uptime, $loadavg1, $mem, $disk;
cba17aeb
DM
79 }
80};
81
3879a8aa
DM
82__PACKAGE__->register_method({
83 name => 'join_cmd',
84 path => 'join_cmd',
85 method => 'GET',
cf9a8dea 86 description => "Prints the command for joining an new node to the cluster. You need to execute the command on the new node.",
3879a8aa
DM
87 parameters => {
88 additionalProperties => 0,
89 properties => {},
90 },
91 returns => { type => 'null' },
92 code => sub {
93 my ($param) = @_;
94
9595814c 95 my $cinfo = PMG::ClusterConfig->new();
3879a8aa 96
9595814c 97 if (scalar(keys %{$cinfo->{ids}})) {
3879a8aa 98
9595814c 99 my $master = $cinfo->{master} ||
3879a8aa
DM
100 die "no master found\n";
101
e16a9efc 102 print "pmgcm join $master->{ip} --fingerprint $master->{fingerprint}\n";
3879a8aa
DM
103
104 } else {
105 die "no cluster defined\n";
106 }
107
108 return undef;
109 }});
110
e411340d
DM
111__PACKAGE__->register_method({
112 name => 'delete',
113 path => 'delete',
114 method => 'GET',
115 description => "Remove a node from the cluster.",
116 parameters => {
117 additionalProperties => 0,
118 properties => {
119 cid => {
120 description => "Cluster Node ID.",
121 type => 'integer',
122 minimum => 1,
123 },
124 },
125 },
126 returns => { type => 'null' },
127 code => sub {
128 my ($param) = @_;
129
130 my $code = sub {
131 my $cinfo = PMG::ClusterConfig->new();
132
133 die "no cluster defined\n" if !scalar(keys %{$cinfo->{ids}});
134
135 my $master = $cinfo->{master} || die "unable to lookup master node\n";
136
137 die "operation not permitted (not master)\n"
138 if $cinfo->{local}->{cid} != $master->{cid};
139
140 my $cid = $param->{cid};
141
142 die "unable to delete master node\n"
143 if $cinfo->{local}->{cid} == $cid;
144
145 die "no such node (cid == $cid does not exists)\n" if !$cinfo->{ids}->{$cid};
146
147 delete $cinfo->{ids}->{$cid};
148
149 $cinfo->write();
150 };
151
152 PMG::ClusterConfig::lock_config($code, "delete cluster node failed");
153
154 return undef;
155 }});
156
e16a9efc
DM
157__PACKAGE__->register_method({
158 name => 'join',
159 path => 'join',
160 method => 'GET',
161 description => "Join a new node to an existing cluster.",
162 parameters => {
163 additionalProperties => 0,
164 properties => {
165 master_ip => {
166 description => "IP address.",
167 type => 'string', format => 'ip',
168 },
169 fingerprint => {
170 description => "SSL certificate fingerprint.",
171 type => 'string',
172 pattern => '^(:?[A-Z0-9][A-Z0-9]:){31}[A-Z0-9][A-Z0-9]$',
173 optional => 1,
174 },
175 },
176 },
177 returns => { type => 'null' },
178 code => sub {
179 my ($param) = @_;
180
181 my $code = sub {
9595814c 182 my $cinfo = PMG::ClusterConfig->new();
e16a9efc 183
56001f4f 184 die "cluster already defined\n" if scalar(keys %{$cinfo->{ids}});
e16a9efc
DM
185
186 my $term = new Term::ReadLine ('pmgcm');
187 my $attribs = $term->Attribs;
188 $attribs->{redisplay_function} = $attribs->{shadow_redisplay};
189 my $password = $term->readline('Enter password: ');
190
191 my $setup = {
192 username => 'root@pam',
193 password => $password,
194 cookie_name => 'PMGAuthCookie',
195 host => $param->{master_ip},
196 };
197 if ($param->{fingerprint}) {
198 $setup->{cached_fingerprints} = {
199 $param->{fingerprint} => 1,
200 };
201 } else {
202 # allow manual fingerprint verification
203 $setup->{manual_verification} = 1;
204 }
205
9595814c 206 PMG::API2::Cluster::cluster_join($cinfo, $setup);
e16a9efc
DM
207 };
208
209 PMG::ClusterConfig::lock_config($code, "cluster join failed");
210
211 return undef;
212 }});
213
d0d3d29a
DM
214__PACKAGE__->register_method({
215 name => 'sync',
216 path => 'sync',
217 method => 'GET',
218 description => "Synchronize cluster configuration.",
219 parameters => {
220 additionalProperties => 0,
221 properties => {
222 master_ip => {
223 description => 'Optional IP address for master node.',
224 type => 'string', format => 'ip',
225 optional => 1,
226 }
227 },
228 },
229 returns => { type => 'null' },
230 code => sub {
231 my ($param) = @_;
232
29ecd597 233 my $cinfo = PMG::ClusterConfig->new();
d0d3d29a 234
8871a5f0
DM
235 my $master_name = undef;
236 my $master_ip = $param->{master_ip};
d0d3d29a 237
9d369141
DM
238 if (!$master_ip && $cinfo->{master}) {
239 $master_ip = $cinfo->{master}->{ip};
240 $master_name = $cinfo->{master}->{name};
8871a5f0 241 }
d0d3d29a 242
8871a5f0 243 die "no master IP specified (use option --master_ip)\n" if !$master_ip;
d0d3d29a 244
9d369141 245 if ($cinfo->{local}->{ip} eq $master_ip) {
809ae8f4
DM
246 print STDERR "local node is master - nothing to do\n";
247 return undef;
248 }
249
8871a5f0 250 print STDERR "syncing master configuration from '${master_ip}'\n";
d0d3d29a 251
809ae8f4
DM
252 PMG::Cluster::sync_config_from_master($master_name, $master_ip);
253
9d369141 254 my $cfg = PMG::Config->new();
9d369141 255
58217372 256 $cfg->rewrite_config(undef, 1);
9d369141 257
809ae8f4 258 return undef;
d0d3d29a
DM
259 }});
260
64244a54
DM
261__PACKAGE__->register_method({
262 name => 'promote',
263 path => 'promote',
264 method => 'POST',
265 description => "Promote current node to become the new master.",
266 parameters => {
267 additionalProperties => 0,
268 properties => {},
269 },
270 returns => { type => 'null' },
271 code => sub {
272 my ($param) = @_;
273
274 my $code = sub {
275 my $cinfo = PMG::ClusterConfig->new();
276
277 die "no cluster defined\n" if !scalar(keys %{$cinfo->{ids}});
278
279 my $master = $cinfo->{master} || die "unable to lookup master node\n";
280
281 die "this node is already master\n"
282 if $cinfo->{local}->{cid} == $master->{cid};
283
284 my $maxcid = $master->{maxcid};
285 $master->{type} = 'node';
286
287 my $newmaster = $cinfo->{local};
288
289 $newmaster->{maxcid} = $maxcid;
290 $newmaster->{type} = 'master';
291
292 $cinfo->{master} = $newmaster;
293
294 $cinfo->write();
295 };
296
297 PMG::ClusterConfig::lock_config($code, "promote new master failed");
298
299 return undef;
300 }});
301
cba17aeb 302our $cmddef = {
150e8421 303 status => [ 'PMG::API2::Cluster', 'status', [], {}, $format_nodelist],
cfdf6608 304 create => [ 'PMG::API2::Cluster', 'create', [], {}, $upid_exit],
e411340d 305 delete => [ __PACKAGE__, 'delete', ['cid']],
e16a9efc 306 join => [ __PACKAGE__, 'join', ['master_ip']],
3879a8aa 307 join_cmd => [ __PACKAGE__, 'join_cmd', []],
d0d3d29a 308 sync => [ __PACKAGE__, 'sync', []],
64244a54 309 promote => [ __PACKAGE__, 'promote', []],
cba17aeb
DM
310};
311
3121;