]>
git.proxmox.com Git - pve-manager.git/blob - PVE/CLI/pvenode.pm
1 package PVE
::CLI
::pvenode
;
7 use PVE
::API2
::ACMEAccount
;
8 use PVE
::API2
::ACMEPlugin
;
9 use PVE
::API2
::Certificates
;
10 use PVE
::API2
::NodeConfig
;
14 use PVE
::ACME
::Challenge
;
17 use PVE
::Exception
qw(raise_param_exc raise);
18 use PVE
::JSONSchema
qw(get_standard_option);
20 use PVE
::RPCEnvironment
;
21 use PVE
::CLIFormatter
;
27 use base
qw(PVE::CLIHandler);
29 my $nodename = PVE
::INotify
::nodename
();
31 sub setup_environment
{
32 PVE
::RPCEnvironment-
>setup_default_cli_env();
37 my $status = PVE
::Tools
::upid_read_status
($upid);
38 print "Task $status\n";
39 exit(PVE
::Tools
::upid_status_is_error
($status) ?
-1 : 0);
45 my $load_file_and_encode = sub {
48 return PVE
::ACME
::Challenge-
>encode_value('string', 'data', PVE
::Tools
::file_get_contents
($filename));
52 'upload_custom_cert' => [
57 ['data', $load_file_and_encode, "File with one key-value pair per line, will be base64url encode for storage in plugin config.", 0],
60 ['data', $load_file_and_encode, "File with one key-value pair per line, will be base64url encode for storage in plugin config.", 0],
64 return $mapping->{$name};
67 __PACKAGE__-
>register_method({
68 name
=> 'acme_register',
69 path
=> 'acme_register',
71 description
=> "Register a new ACME account with a compatible CA.",
73 additionalProperties
=> 0,
75 name
=> get_standard_option
('pve-acme-account-name'),
76 contact
=> get_standard_option
('pve-acme-account-contact'),
77 directory
=> get_standard_option
('pve-acme-directory-url', {
82 returns
=> { type
=> 'null' },
86 my $custom_directory = 0;
87 if (!$param->{directory
}) {
88 my $directories = PVE
::API2
::ACMEAccount-
>get_directories({});
89 print "Directory endpoints:\n";
91 while ($i < @$directories) {
92 print $i, ") ", $directories->[$i]->{name
}, " (", $directories->[$i]->{url
}, ")\n";
95 print $i, ") Custom\n";
97 my $term = Term
::ReadLine-
>new('pvenode');
98 my $get_dir_selection = sub {
99 my $selection = $term->readline("Enter selection: ");
100 if ($selection =~ /^(\d+)$/) {
102 if ($selection == $i) {
103 $param->{directory
} = $term->readline("Enter custom URL: ");
104 $custom_directory = 1;
106 } elsif ($selection < $i && $selection >= 0) {
107 $param->{directory
} = $directories->[$selection]->{url
};
111 print "Invalid selection.\n";
115 while (!$param->{directory
}) {
116 die "Aborting.\n" if $attempts > 3;
117 $get_dir_selection->();
121 print "\nAttempting to fetch Terms of Service from '$param->{directory}'..\n";
122 my $meta = PVE
::API2
::ACMEAccount-
>get_meta({ directory
=> $param->{directory
} });
123 if ($meta->{termsOfService
}) {
124 my $tos = $meta->{termsOfService
};
125 print "Terms of Service: $tos\n";
126 my $term = Term
::ReadLine-
>new('pvenode');
127 my $agreed = $term->readline('Do you agree to the above terms? [y|N]: ');
128 die "Cannot continue without agreeing to ToS, aborting.\n"
129 if ($agreed !~ /^y$/i);
131 $param->{tos_url
} = $tos;
133 print "No Terms of Service found, proceeding.\n";
136 my $eab_enabled = $meta->{externalAccountRequired
};
137 if (!$eab_enabled && $custom_directory) {
138 my $term = Term
::ReadLine-
>new('pvenode');
139 my $agreed = $term->readline('Do you want to use external account binding? [y|N]: ');
140 $eab_enabled = ($agreed =~ /^y$/i);
141 } elsif ($eab_enabled) {
142 print "The CA requires external account binding.\n";
145 print "You should have received a key id and a key from your CA.\n";
146 my $term = Term
::ReadLine-
>new('pvenode');
147 my $eab_kid = $term->readline('Enter EAB key id: ');
148 my $eab_hmac_key = $term->readline('Enter EAB key: ');
150 $param->{'eab-kid'} = $eab_kid;
151 $param->{'eab-hmac-key'} = $eab_hmac_key;
154 print "\nAttempting to register account with '$param->{directory}'..\n";
156 $upid_exit->(PVE
::API2
::ACMEAccount-
>register_account($param));
159 my $print_cert_info = sub {
160 my ($schema, $cert, $options) = @_;
162 my $order = [qw(filename fingerprint subject issuer notbefore notafter public-key-type public-key-bits san)];
163 PVE
::CLIFormatter
::print_api_result
(
164 $cert, $schema, $order, { %$options, noheader
=> 1, sort_key
=> 0 });
169 get
=> [ 'PVE::API2::NodeConfig', 'get_config', [], { node
=> $nodename }, sub {
171 print PVE
::NodeConfig
::write_node_config
($res);
173 set
=> [ 'PVE::API2::NodeConfig', 'set_options', [], { node
=> $nodename } ],
176 startall
=> [ 'PVE::API2::Nodes::Nodeinfo', 'startall', [], { node
=> $nodename } ],
177 stopall
=> [ 'PVE::API2::Nodes::Nodeinfo', 'stopall', [], { node
=> $nodename } ],
178 migrateall
=> [ 'PVE::API2::Nodes::Nodeinfo', 'migrateall', [ 'target' ], { node
=> $nodename } ],
181 info
=> [ 'PVE::API2::Certificates', 'info', [], { node
=> $nodename }, sub {
182 my ($res, $schema, $options) = @_;
184 if (!$options->{'output-format'} || $options->{'output-format'} eq 'text') {
185 for my $cert (sort { $a->{filename
} cmp $b->{filename
} } @$res) {
186 $print_cert_info->($schema->{items
}, $cert, $options);
189 PVE
::CLIFormatter
::print_api_result
($res, $schema, undef, $options);
192 }, $PVE::RESTHandler
::standard_output_options
],
193 set
=> [ 'PVE::API2::Certificates', 'upload_custom_cert', ['certificates', 'key'], { node
=> $nodename }, sub {
194 my ($res, $schema, $options) = @_;
195 $print_cert_info->($schema, $res, $options);
196 }, $PVE::RESTHandler
::standard_output_options
],
197 delete => [ 'PVE::API2::Certificates', 'remove_custom_cert', ['restart'], { node
=> $nodename } ],
201 list
=> [ 'PVE::API2::Tasks', 'node_tasks', [], { node
=> $nodename }, sub {
202 my ($data, $schema, $options) = @_;
203 foreach my $task (@$data) {
204 if (!defined($task->{status
})) {
205 $task->{status
} = 'UNKNOWN';
206 # RUNNING is set by the API call and needs to be checked explicitly
207 } elsif (PVE
::Tools
::upid_status_is_error
($task->{status
}) &&
208 $task->{status
} ne 'RUNNING')
210 $task->{status
} = 'ERROR';
213 PVE
::CLIFormatter
::print_api_result
($data, $schema, ['upid', 'type', 'id', 'user', 'starttime', 'endtime', 'status' ], $options);
214 }, $PVE::RESTHandler
::standard_output_options
],
215 status
=> [ 'PVE::API2::Tasks', 'read_task_status', [ 'upid' ], { node
=> $nodename }, sub {
216 my ($data, $schema, $options) = @_;
217 PVE
::CLIFormatter
::print_api_result
($data, $schema, undef, $options);
218 }, $PVE::RESTHandler
::standard_output_options
],
219 # set limit to 1000000, so we see the whole log, not only the first 50 lines by default
220 log => [ 'PVE::API2::Tasks', 'read_task_log', [ 'upid' ], { node
=> $nodename, limit
=> 1000000 }, sub {
221 my ($data, $resultprops) = @_;
222 foreach my $line (@$data) {
223 print $line->{t
} . "\n";
230 list
=> [ 'PVE::API2::ACMEAccount', 'account_index', [], {}, sub {
232 for my $acc (@$res) {
233 print "$acc->{name}\n";
236 register
=> [ __PACKAGE__
, 'acme_register', ['name', 'contact'], {}, $upid_exit ],
237 deactivate
=> [ 'PVE::API2::ACMEAccount', 'deactivate_account', ['name'], {}, $upid_exit ],
238 info
=> [ 'PVE::API2::ACMEAccount', 'get_account', ['name'], {}, sub {
239 my ($data, $schema, $options) = @_;
240 PVE
::CLIFormatter
::print_api_result
($data, $schema, undef, $options);
241 }, $PVE::RESTHandler
::standard_output_options
],
242 update
=> [ 'PVE::API2::ACMEAccount', 'update_account', ['name'], {}, $upid_exit ],
245 order
=> [ 'PVE::API2::ACME', 'new_certificate', [], { node
=> $nodename }, $upid_exit ],
246 renew
=> [ 'PVE::API2::ACME', 'renew_certificate', [], { node
=> $nodename }, $upid_exit ],
247 revoke
=> [ 'PVE::API2::ACME', 'revoke_certificate', [], { node
=> $nodename }, $upid_exit ],
250 list
=> [ 'PVE::API2::ACMEPlugin', 'index', [], {}, sub {
251 my ($data, $schema, $options) = @_;
252 PVE
::CLIFormatter
::print_api_result
($data, $schema, undef, $options);
253 }, $PVE::RESTHandler
::standard_output_options
],
254 config
=> [ 'PVE::API2::ACMEPlugin', 'get_plugin_config', ['id'], {}, sub {
255 my ($data, $schema, $options) = @_;
256 PVE
::CLIFormatter
::print_api_result
($data, $schema, undef, $options);
257 }, $PVE::RESTHandler
::standard_output_options
],
258 add
=> [ 'PVE::API2::ACMEPlugin', 'add_plugin', ['type', 'id'] ],
259 set
=> [ 'PVE::API2::ACMEPlugin', 'update_plugin', ['id'] ],
260 remove
=> [ 'PVE::API2::ACMEPlugin', 'delete_plugin', ['id'] ],
265 wakeonlan
=> [ 'PVE::API2::Nodes::Nodeinfo', 'wakeonlan', [ 'node' ], {}, sub {
268 print "Wake on LAN packet send for '$mac_addr'\n";