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