]>
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 | ||
c9138c03 | 31 | PVE::APIClient::JSONSchema::register_standard_option('pveclient-remote-name', { |
184877d4 DM |
32 | description => "The name of the remote.", |
33 | type => 'string', | |
a6dab5b8 | 34 | pattern => $remote_namne_regex, |
184877d4 DM |
35 | completion => $complete_remote_name, |
36 | }); | |
37 | ||
69aa81b3 DM |
38 | my $defaultData = { |
39 | propertyList => { | |
40 | type => { | |
41 | description => "Section type.", | |
42 | optional => 1, | |
43 | }, | |
69aa81b3 DM |
44 | }, |
45 | }; | |
06039c7f | 46 | |
69aa81b3 DM |
47 | sub private { |
48 | return $defaultData; | |
06039c7f | 49 | } |
aa570b38 | 50 | |
69aa81b3 DM |
51 | sub config_filename { |
52 | my ($class) = @_; | |
aa570b38 | 53 | |
bcd2a4df | 54 | my $dir = PVE::APIClient::Helpers::configuration_directory(); |
aa5833d6 | 55 | |
bcd2a4df | 56 | return "$dir/config"; |
06039c7f | 57 | } |
aa570b38 | 58 | |
b4007425 DM |
59 | sub lock_config { |
60 | my ($class, $timeout, $code, @param) = @_; | |
61 | ||
ace4a6df RJ |
62 | my $dir = PVE::APIClient::Helpers::configuration_directory(); |
63 | make_path($dir); | |
64 | ||
65 | my $filename = "$dir/.config.lck"; | |
b4007425 DM |
66 | |
67 | my $res = PVE::APIClient::Tools::lock_file($filename, $timeout, $code, @param); | |
68 | ||
69 | die $@ if $@; | |
70 | ||
71 | return $res; | |
72 | } | |
73 | ||
a6dab5b8 DM |
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 | ||
69aa81b3 DM |
100 | sub load { |
101 | my ($class) = @_; | |
06039c7f | 102 | |
69aa81b3 | 103 | my $filename = $class->config_filename(); |
aa570b38 | 104 | |
69aa81b3 | 105 | my $raw = ''; |
06039c7f | 106 | |
69aa81b3 DM |
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 | } | |
06039c7f | 112 | |
69aa81b3 DM |
113 | $raw = file_get_contents($filename); |
114 | } | |
06039c7f | 115 | |
69aa81b3 DM |
116 | return $class->parse_config($filename, $raw); |
117 | } | |
06039c7f | 118 | |
69aa81b3 DM |
119 | sub save { |
120 | my ($class, $cfg) = @_; | |
06039c7f | 121 | |
69aa81b3 | 122 | my $filename = $class->config_filename(); |
a6dab5b8 | 123 | |
bcd2a4df DM |
124 | make_path(dirname($filename)); |
125 | ||
a6dab5b8 | 126 | $cfg->{order}->{$defaults_section} = -1; # write as first section |
69aa81b3 | 127 | my $raw = $class->write_config($filename, $cfg); |
06039c7f | 128 | |
69aa81b3 | 129 | file_set_contents($filename, $raw, 0600); |
06039c7f RJ |
130 | } |
131 | ||
a6dab5b8 DM |
132 | sub get_defaults { |
133 | my ($class, $cfg) = @_; | |
134 | ||
135 | $cfg->{ids}->{$defaults_section} //= {}; | |
136 | ||
137 | return $cfg->{ids}->{$defaults_section}; | |
138 | } | |
139 | ||
69aa81b3 DM |
140 | sub lookup_remote { |
141 | my ($class, $cfg, $name, $noerr) = @_; | |
06039c7f | 142 | |
69aa81b3 | 143 | my $data = $cfg->{ids}->{$name}; |
06039c7f | 144 | |
69aa81b3 | 145 | return $data if $noerr || defined($data); |
06039c7f | 146 | |
69aa81b3 | 147 | die "unknown remote \"$name\"\n"; |
06039c7f RJ |
148 | } |
149 | ||
150 | sub remote_conn { | |
69aa81b3 | 151 | my ($class, $cfg, $remote) = @_; |
06039c7f | 152 | |
69aa81b3 | 153 | my $section = $class->lookup_remote($cfg, $remote); |
8842464b | 154 | |
05d328ef DM |
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 | ||
8842464b | 179 | my $password = $section->{password}; |
8842464b | 180 | |
05d328ef DM |
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->(); | |
06039c7f | 199 | } |
05d328ef | 200 | } |
aa570b38 | 201 | |
05d328ef | 202 | PVE::APIClient::Helpers::ticket_cache_update($remote, $conn->{ticket}); |
aa570b38 | 203 | |
06039c7f | 204 | return $conn; |
aa570b38 | 205 | } |
a31c7b27 | 206 | |
a6dab5b8 DM |
207 | package PVE::APIClient::RemoteConfig; |
208 | ||
209 | use strict; | |
210 | use warnings; | |
211 | ||
c9138c03 DM |
212 | use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option); |
213 | use PVE::APIClient::SectionConfig; | |
a6dab5b8 DM |
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 | ||
c9138c03 | 279 | use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option); |
a6dab5b8 DM |
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 | ||
69aa81b3 | 296 | __PACKAGE__->register(); |
a6dab5b8 DM |
297 | |
298 | ||
299 | PVE::APIClient::Config->init(); | |
69aa81b3 | 300 | |
a31c7b27 | 301 | 1; |