]>
git.proxmox.com Git - pmg-api.git/blob - PMG/CLI/pmgcm.pm
1 package PMG
::CLI
::pmgcm
;
7 use POSIX
qw(strftime);
11 use PVE
::Tools
qw(extract_param);
17 use PMG
::RESTEnvironment
;
22 use PMG
::ClusterConfig
;
23 use PMG
::API2
::Cluster
;
25 use base
qw(PVE::CLIHandler);
27 sub setup_environment
{
28 PMG
::RESTEnvironment-
>setup_default_cli_env();
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);
38 my $status = PVE
::Tools
::upid_read_status
($upid);
39 exit($status eq 'OK' ?
0 : -1);
42 my $format_nodelist = sub {
46 print "no cluster defined\n";
50 print "NAME(CID)--------------IPADDRESS----ROLE-STATE---------UPTIME---LOAD----MEM---DISK\n";
51 foreach my $ni (@$res) {
53 $state = 'S' if !$ni->{insync
};
55 if (my $err = $ni->{conn_error
}) {
57 $state = "ERROR: $err";
60 my $uptime = $ni->{uptime
} ? PMG
::Utils
::format_uptime
($ni->{uptime
}) : '-';
63 if (my $d = $ni->{loadavg
}) {
68 if (my $d = $ni->{memory
}) {
69 $mem = int(0.5 + ($d->{used
}*100/$d->{total
}));
72 if (my $d = $ni->{rootfs
}) {
73 $disk = int(0.5 + ($d->{used
}*100/$d->{total
}));
76 printf "%-20s %-15s %-6s %1s %15s %6s %5s%% %5s%%\n",
77 "$ni->{name}($ni->{cid})", $ni->{ip
}, $ni->{type
},
78 $state, $uptime, $loadavg1, $mem, $disk;
82 __PACKAGE__-
>register_method({
86 description
=> "Prints the command for joining an new node to the cluster. You need to execute the command on the new node.",
88 additionalProperties
=> 0,
91 returns
=> { type
=> 'null' },
95 my $cinfo = PMG
::ClusterConfig-
>new();
97 if (scalar(keys %{$cinfo->{ids
}})) {
99 my $master = $cinfo->{master
} ||
100 die "no master found\n";
102 print "pmgcm join $master->{ip} --fingerprint $master->{fingerprint}\n";
105 die "no cluster defined\n";
111 __PACKAGE__-
>register_method({
115 description
=> "Remove a node from the cluster.",
117 additionalProperties
=> 0,
120 description
=> "Cluster Node ID.",
126 returns
=> { type
=> 'null' },
131 my $cinfo = PMG
::ClusterConfig-
>new();
133 die "no cluster defined\n" if !scalar(keys %{$cinfo->{ids
}});
135 my $master = $cinfo->{master
} || die "unable to lookup master node\n";
137 die "operation not permitted (not master)\n"
138 if $cinfo->{local}->{cid
} != $master->{cid
};
140 my $cid = $param->{cid
};
142 die "unable to delete master node\n"
143 if $cinfo->{local}->{cid
} == $cid;
145 die "no such node (cid == $cid does not exists)\n" if !$cinfo->{ids
}->{$cid};
147 delete $cinfo->{ids
}->{$cid};
152 PMG
::ClusterConfig
::lock_config
($code, "delete cluster node failed");
157 __PACKAGE__-
>register_method({
161 description
=> "Join a new node to an existing cluster.",
163 additionalProperties
=> 0,
166 description
=> "IP address.",
167 type
=> 'string', format
=> 'ip',
170 description
=> "SSL certificate fingerprint.",
172 pattern
=> '^(:?[A-Z0-9][A-Z0-9]:){31}[A-Z0-9][A-Z0-9]$',
177 returns
=> { type
=> 'null' },
182 my $cinfo = PMG
::ClusterConfig-
>new();
184 die "cluster alreayd defined\n" if scalar(keys %{$cinfo->{ids
}});
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: ');
192 username
=> 'root@pam',
193 password
=> $password,
194 cookie_name
=> 'PMGAuthCookie',
195 host
=> $param->{master_ip
},
197 if ($param->{fingerprint
}) {
198 $setup->{cached_fingerprints
} = {
199 $param->{fingerprint
} => 1,
202 # allow manual fingerprint verification
203 $setup->{manual_verification
} = 1;
206 PMG
::API2
::Cluster
::cluster_join
($cinfo, $setup);
209 PMG
::ClusterConfig
::lock_config
($code, "cluster join failed");
214 __PACKAGE__-
>register_method({
218 description
=> "Synchronize cluster configuration.",
220 additionalProperties
=> 0,
223 description
=> 'Optional IP address for master node.',
224 type
=> 'string', format
=> 'ip',
229 returns
=> { type
=> 'null' },
233 my $cinfo = PMG
::ClusterConfig-
>new();
235 my $master_name = undef;
236 my $master_ip = $param->{master_ip
};
238 if (!$master_ip && $cinfo->{master
}) {
239 $master_ip = $cinfo->{master
}->{ip
};
240 $master_name = $cinfo->{master
}->{name
};
243 die "no master IP specified (use option --master_ip)\n" if !$master_ip;
245 if ($cinfo->{local}->{ip
} eq $master_ip) {
246 print STDERR
"local node is master - nothing to do\n";
250 print STDERR
"syncing master configuration from '${master_ip}'\n";
252 PMG
::Cluster
::sync_config_from_master
($master_name, $master_ip);
254 my $cfg = PMG
::Config-
>new();
256 $cfg->rewrite_config(undef, 1);
261 __PACKAGE__-
>register_method({
265 description
=> "Promote current node to become the new master.",
267 additionalProperties
=> 0,
270 returns
=> { type
=> 'null' },
275 my $cinfo = PMG
::ClusterConfig-
>new();
277 die "no cluster defined\n" if !scalar(keys %{$cinfo->{ids
}});
279 my $master = $cinfo->{master
} || die "unable to lookup master node\n";
281 die "this node is already master\n"
282 if $cinfo->{local}->{cid
} == $master->{cid
};
284 my $maxcid = $master->{maxcid
};
285 $master->{type
} = 'node';
287 my $newmaster = $cinfo->{local};
289 $newmaster->{maxcid
} = $maxcid;
290 $newmaster->{type
} = 'master';
292 $cinfo->{master
} = $newmaster;
297 PMG
::ClusterConfig
::lock_config
($code, "promote new master failed");
303 status
=> [ 'PMG::API2::Cluster', 'status', [], {}, $format_nodelist],
304 create
=> [ 'PMG::API2::Cluster', 'create', [], {}, $upid_exit],
305 delete => [ __PACKAGE__
, 'delete', ['cid']],
306 join => [ __PACKAGE__
, 'join', ['master_ip']],
307 join_cmd
=> [ __PACKAGE__
, 'join_cmd', []],
308 sync
=> [ __PACKAGE__
, 'sync', []],
309 promote
=> [ __PACKAGE__
, 'promote', []],