]>
Commit | Line | Data |
---|---|---|
a31c7b27 DM |
1 | package PVE::APIClient::Config; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use JSON; | |
bcd2a4df DM |
6 | use File::Basename qw(dirname); |
7 | use File::Path qw(make_path); | |
a31c7b27 | 8 | |
bcd2a4df | 9 | use PVE::APIClient::Helpers; |
c9138c03 DM |
10 | use PVE::APIClient::JSONSchema; |
11 | use PVE::APIClient::SectionConfig; | |
269f8ffa | 12 | use PVE::APIClient::PTY; |
ca3269f4 | 13 | use PVE::APIClient::Tools qw(file_get_contents file_set_contents); |
a31c7b27 | 14 | |
c9138c03 | 15 | use base qw(PVE::APIClient::SectionConfig); |
69aa81b3 | 16 | |
a6dab5b8 DM |
17 | my $remote_namne_regex = qw(\w+); |
18 | ||
19 | my $defaults_section = '!DEFAULTS'; | |
20 | ||
184877d4 DM |
21 | my $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 |
31 | PVE::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 | 39 | PVE::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 |
46 | my $defaultData = { |
47 | propertyList => { | |
48 | type => { | |
49 | description => "Section type.", | |
50 | optional => 1, | |
51 | }, | |
69aa81b3 DM |
52 | }, |
53 | }; | |
06039c7f | 54 | |
69aa81b3 DM |
55 | sub private { |
56 | return $defaultData; | |
06039c7f | 57 | } |
aa570b38 | 58 | |
69aa81b3 DM |
59 | sub 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 |
67 | sub 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 |
82 | sub 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 | ||
92 | sub 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 |
108 | sub 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 |
127 | sub 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 |
140 | sub get_defaults { |
141 | my ($class, $cfg) = @_; | |
142 | ||
143 | $cfg->{ids}->{$defaults_section} //= {}; | |
144 | ||
145 | return $cfg->{ids}->{$defaults_section}; | |
146 | } | |
147 | ||
69aa81b3 DM |
148 | sub 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 | ||
158 | sub 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 |
215 | package PVE::APIClient::RemoteConfig; |
216 | ||
217 | use strict; | |
218 | use warnings; | |
219 | ||
c9138c03 DM |
220 | use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option); |
221 | use PVE::APIClient::SectionConfig; | |
a6dab5b8 DM |
222 | |
223 | use base qw( PVE::APIClient::Config); | |
224 | ||
225 | sub type { | |
226 | return 'remote'; | |
227 | } | |
228 | ||
229 | sub 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 | ||
267 | sub 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 | ||
282 | package PVE::APIClient::DefaultsConfig; | |
283 | ||
284 | use strict; | |
285 | use warnings; | |
286 | ||
c9138c03 | 287 | use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option); |
a6dab5b8 DM |
288 | |
289 | use base qw( PVE::APIClient::Config); | |
290 | ||
291 | ||
292 | sub type { | |
293 | return 'defaults'; | |
294 | } | |
295 | ||
296 | sub 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 | ||
307 | PVE::APIClient::Config->init(); | |
69aa81b3 | 308 | |
a31c7b27 | 309 | 1; |