]> git.proxmox.com Git - pve-client.git/blame - PVE/APIClient/Config.pm
Handle errors on tasks correctly with a die,
[pve-client.git] / PVE / APIClient / Config.pm
CommitLineData
a31c7b27
DM
1package PVE::APIClient::Config;
2
3use strict;
4use warnings;
5use JSON;
bcd2a4df
DM
6use File::Basename qw(dirname);
7use File::Path qw(make_path);
a31c7b27 8
bcd2a4df 9use PVE::APIClient::Helpers;
c9138c03
DM
10use PVE::APIClient::JSONSchema;
11use PVE::APIClient::SectionConfig;
269f8ffa 12use PVE::APIClient::PTY;
ca3269f4 13use PVE::APIClient::Tools qw(file_get_contents file_set_contents);
a31c7b27 14
c9138c03 15use base qw(PVE::APIClient::SectionConfig);
69aa81b3 16
a6dab5b8
DM
17my $remote_namne_regex = qw(\w+);
18
19my $defaults_section = '!DEFAULTS';
20
184877d4
DM
21my $complete_remote_name = sub {
22
69aa81b3 23 my $config = PVE::APIClient::Config->load();
a6dab5b8
DM
24 my $list = [];
25 foreach my $name (keys %{$config->{ids}}) {
26 push @$list, $name if $name ne $defaults_section;
27 }
28 return $list;
184877d4
DM
29};
30
cbfba90b
DM
31PVE::APIClient::JSONSchema::register_standard_option('pveclient-output-format', {
32 type => 'string',
33 description => 'Output format.',
b5aeedb0 34 enum => [ 'text', 'json' ],
cbfba90b 35 optional => 1,
b5aeedb0 36 default => 'text',
cbfba90b
DM
37});
38
c9138c03 39PVE::APIClient::JSONSchema::register_standard_option('pveclient-remote-name', {
184877d4
DM
40 description => "The name of the remote.",
41 type => 'string',
a6dab5b8 42 pattern => $remote_namne_regex,
184877d4
DM
43 completion => $complete_remote_name,
44});
45
69aa81b3
DM
46my $defaultData = {
47 propertyList => {
48 type => {
49 description => "Section type.",
50 optional => 1,
51 },
69aa81b3
DM
52 },
53};
06039c7f 54
69aa81b3
DM
55sub private {
56 return $defaultData;
06039c7f 57}
aa570b38 58
69aa81b3
DM
59sub config_filename {
60 my ($class) = @_;
aa570b38 61
bcd2a4df 62 my $dir = PVE::APIClient::Helpers::configuration_directory();
aa5833d6 63
bcd2a4df 64 return "$dir/config";
06039c7f 65}
aa570b38 66
b4007425
DM
67sub lock_config {
68 my ($class, $timeout, $code, @param) = @_;
69
ace4a6df
RJ
70 my $dir = PVE::APIClient::Helpers::configuration_directory();
71 make_path($dir);
72
73 my $filename = "$dir/.config.lck";
b4007425
DM
74
75 my $res = PVE::APIClient::Tools::lock_file($filename, $timeout, $code, @param);
76
77 die $@ if $@;
78
79 return $res;
80}
81
a6dab5b8
DM
82sub format_section_header {
83 my ($class, $type, $sectionId, $scfg, $done_hash) = @_;
84
85 if ($type eq 'defaults') {
86 return "defaults:\n";
87 } else {
88 return "$type: $sectionId\n";
89 }
90}
91
92sub parse_section_header {
93 my ($class, $line) = @_;
94
95 if ($line =~ m/^defaults:\s*$/) {
96 return ('defaults', $defaults_section, undef, {});
97 } elsif ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
98 my ($type, $name) = (lc($1), $2);
99 eval {
100 die "invalid remote name '$name'\n"
101 if $name eq $defaults_section || $name !~ m/^$remote_namne_regex$/;
102 };
103 return ($type, $name, $@, {});
104 }
105 return undef;
106}
107
69aa81b3
DM
108sub load {
109 my ($class) = @_;
06039c7f 110
69aa81b3 111 my $filename = $class->config_filename();
aa570b38 112
69aa81b3 113 my $raw = '';
06039c7f 114
69aa81b3
DM
115 if (-e $filename) {
116 my $filemode = (stat($filename))[2] & 07777;
117 if ($filemode != 0600) {
118 die sprintf "wrong permissions on '$filename' %04o (expected 0600)\n", $filemode;
119 }
06039c7f 120
69aa81b3
DM
121 $raw = file_get_contents($filename);
122 }
06039c7f 123
69aa81b3
DM
124 return $class->parse_config($filename, $raw);
125}
06039c7f 126
69aa81b3
DM
127sub save {
128 my ($class, $cfg) = @_;
06039c7f 129
69aa81b3 130 my $filename = $class->config_filename();
a6dab5b8 131
bcd2a4df
DM
132 make_path(dirname($filename));
133
a6dab5b8 134 $cfg->{order}->{$defaults_section} = -1; # write as first section
69aa81b3 135 my $raw = $class->write_config($filename, $cfg);
06039c7f 136
69aa81b3 137 file_set_contents($filename, $raw, 0600);
06039c7f
RJ
138}
139
a6dab5b8
DM
140sub get_defaults {
141 my ($class, $cfg) = @_;
142
143 $cfg->{ids}->{$defaults_section} //= {};
144
145 return $cfg->{ids}->{$defaults_section};
146}
147
69aa81b3
DM
148sub lookup_remote {
149 my ($class, $cfg, $name, $noerr) = @_;
06039c7f 150
69aa81b3 151 my $data = $cfg->{ids}->{$name};
06039c7f 152
69aa81b3 153 return $data if $noerr || defined($data);
06039c7f 154
69aa81b3 155 die "unknown remote \"$name\"\n";
06039c7f
RJ
156}
157
158sub remote_conn {
69aa81b3 159 my ($class, $cfg, $remote) = @_;
06039c7f 160
69aa81b3 161 my $section = $class->lookup_remote($cfg, $remote);
8842464b 162
05d328ef
DM
163 my $trylogin = sub {
164 my ($ticket_or_password) = @_;
165
166 if (!defined($ticket_or_password)) {
167 $ticket_or_password = PVE::APIClient::PTY::read_password("Remote password: ")
168 }
169
170 my $setup = {
171 username => $section->{username},
172 password => $ticket_or_password,
173 host => $section->{host},
174 port => $section->{port} // 8006,
175 cached_fingerprints => {
176 $section->{fingerprint} => 1,
177 }
178 };
179
180 my $conn = PVE::APIClient::LWP->new(%$setup);
181
182 $conn->login();
183
184 return $conn;
185 };
186
8842464b 187 my $password = $section->{password};
8842464b 188
05d328ef
DM
189 my $conn;
190
191 if (defined($password)) {
192 $conn = $trylogin->($password);
193 } else {
194
195 if (my $ticket = PVE::APIClient::Helpers::ticket_cache_lookup($remote)) {
196 eval { $conn = $trylogin->($ticket); };
197 if (my $err = $@) {
198 PVE::APIClient::Helpers::ticket_cache_update($remote, undef);
199 if (ref($err) && (ref($err) eq 'PVE::APIClient::Exception') && ($err->{code} == 401)) {
200 $conn = $trylogin->();
201 } else {
202 die $err;
203 }
204 }
205 } else {
206 $conn = $trylogin->();
06039c7f 207 }
05d328ef 208 }
aa570b38 209
05d328ef 210 PVE::APIClient::Helpers::ticket_cache_update($remote, $conn->{ticket});
aa570b38 211
06039c7f 212 return $conn;
aa570b38 213}
a31c7b27 214
a6dab5b8
DM
215package PVE::APIClient::RemoteConfig;
216
217use strict;
218use warnings;
219
c9138c03
DM
220use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option);
221use PVE::APIClient::SectionConfig;
a6dab5b8
DM
222
223use base qw( PVE::APIClient::Config);
224
225sub type {
226 return 'remote';
227}
228
229sub properties {
230 return {
231 name => get_standard_option('pveclient-remote-name'),
232 host => {
233 description => "The host.",
234 type => 'string', format => 'address',
235 optional => 1,
236 },
237 username => {
238 description => "The username.",
239 type => 'string',
240 optional => 1,
241 },
242 password => {
243 description => "The users password.",
244 type => 'string',
245 optional => 1,
246 },
247 port => {
248 description => "The port.",
249 type => 'integer',
250 optional => 1,
251 default => 8006,
252 },
253 fingerprint => {
254 description => "Fingerprint.",
255 type => 'string',
256 optional => 1,
257 },
258 comment => {
259 description => "Description.",
260 type => 'string',
261 optional => 1,
262 maxLength => 4096,
263 },
264 };
265}
266
267sub options {
268 return {
269 name => { optional => 0 },
270 host => { optional => 0 },
271 comment => { optional => 1 },
272 username => { optional => 0 },
273 password => { optional => 1 },
274 port => { optional => 1 },
275 fingerprint => { optional => 1 },
276 };
277}
278
279__PACKAGE__->register();
280
281
282package PVE::APIClient::DefaultsConfig;
283
284use strict;
285use warnings;
286
c9138c03 287use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option);
a6dab5b8
DM
288
289use base qw( PVE::APIClient::Config);
290
291
292sub type {
293 return 'defaults';
294}
295
296sub options {
297 return {
298 name => { optional => 1 },
299 username => { optional => 1 },
300 port => { optional => 1 },
301 };
302}
303
69aa81b3 304__PACKAGE__->register();
a6dab5b8
DM
305
306
307PVE::APIClient::Config->init();
69aa81b3 308
a31c7b27 3091;