X-Git-Url: https://git.proxmox.com/?p=pve-client.git;a=blobdiff_plain;f=PVE%2FAPIClient%2FConfig.pm;h=5437be656ea1e2588f3d5528b434a0404f4f731e;hp=8fa769140390061045b6e40c577c344086071c54;hb=faa47c20e27c8d4050594e9abaa2c65d792a7eda;hpb=8842464b81a452996373e5d21205282eeef66888 diff --git a/PVE/APIClient/Config.pm b/PVE/APIClient/Config.pm index 8fa7691..5437be6 100644 --- a/PVE/APIClient/Config.pm +++ b/PVE/APIClient/Config.pm @@ -3,85 +3,47 @@ package PVE::APIClient::Config; use strict; use warnings; use JSON; +use File::Basename qw(dirname); +use File::Path qw(make_path); -use PVE::JSONSchema qw(register_standard_option get_standard_option); -use PVE::SectionConfig; -use PVE::Tools qw(file_get_contents file_set_contents); +use PVE::APIClient::Helpers; +use PVE::APIClient::JSONSchema; +use PVE::APIClient::SectionConfig; +use PVE::APIClient::PTY; +use PVE::APIClient::Tools qw(file_get_contents file_set_contents); -use base qw(PVE::SectionConfig); +use base qw(PVE::APIClient::SectionConfig); + +my $remote_namne_regex = qw(\w+); + +my $defaults_section = '!DEFAULTS'; my $complete_remote_name = sub { my $config = PVE::APIClient::Config->load(); - return [keys %{$config->{ids}}]; + my $list = []; + foreach my $name (keys %{$config->{ids}}) { + push @$list, $name if $name ne $defaults_section; + } + return $list; }; -register_standard_option('pveclient-remote-name', { +PVE::APIClient::JSONSchema::register_standard_option('pveclient-remote-name', { description => "The name of the remote.", type => 'string', - pattern => qr(\w+), + pattern => $remote_namne_regex, completion => $complete_remote_name, }); - my $defaultData = { propertyList => { type => { description => "Section type.", optional => 1, }, - name => get_standard_option('pveclient-remote-name'), - host => { - description => "The host.", - type => 'string', format => 'address', - optional => 1, - }, - username => { - description => "The username.", - type => 'string', - optional => 1, - }, - password => { - description => "The users password.", - type => 'string', - optional => 1, - }, - port => { - description => "The port.", - type => 'integer', - optional => 1, - default => 8006, - }, - fingerprint => { - description => "Fingerprint.", - type => 'string', - optional => 1, - }, - comment => { - description => "Description.", - type => 'string', - optional => 1, - maxLength => 4096, - }, }, }; -sub type { - return 'remote'; -} - -sub options { - return { - name => { optional => 0 }, - host => { optional => 0 }, - comment => { optional => 1 }, - username => { optional => 0 }, - password => { optional => 1 }, - port => { optional => 1 }, - fingerprint => { optional => 1 }, - }; -} - sub private { return $defaultData; } @@ -89,11 +51,50 @@ sub private { sub config_filename { my ($class) = @_; - my $home = $ENV{HOME}; + my $dir = PVE::APIClient::Helpers::configuration_directory(); + + return "$dir/config"; +} + +sub lock_config { + my ($class, $timeout, $code, @param) = @_; + + my $dir = PVE::APIClient::Helpers::configuration_directory(); + make_path($dir); + + my $filename = "$dir/.config.lck"; + + my $res = PVE::APIClient::Tools::lock_file($filename, $timeout, $code, @param); + + die $@ if $@; + + return $res; +} - die "environment HOME not set\n" if !defined($home); +sub format_section_header { + my ($class, $type, $sectionId, $scfg, $done_hash) = @_; - return "$home/.pveclient"; + if ($type eq 'defaults') { + return "defaults:\n"; + } else { + return "$type: $sectionId\n"; + } +} + +sub parse_section_header { + my ($class, $line) = @_; + + if ($line =~ m/^defaults:\s*$/) { + return ('defaults', $defaults_section, undef, {}); + } elsif ($line =~ m/^(\S+):\s*(\S+)\s*$/) { + my ($type, $name) = (lc($1), $2); + eval { + die "invalid remote name '$name'\n" + if $name eq $defaults_section || $name !~ m/^$remote_namne_regex$/; + }; + return ($type, $name, $@, {}); + } + return undef; } sub load { @@ -119,11 +120,23 @@ sub save { my ($class, $cfg) = @_; my $filename = $class->config_filename(); + + make_path(dirname($filename)); + + $cfg->{order}->{$defaults_section} = -1; # write as first section my $raw = $class->write_config($filename, $cfg); file_set_contents($filename, $raw, 0600); } +sub get_defaults { + my ($class, $cfg) = @_; + + $cfg->{ids}->{$defaults_section} //= {}; + + return $cfg->{ids}->{$defaults_section}; +} + sub lookup_remote { my ($class, $cfg, $name, $noerr) = @_; @@ -139,27 +152,150 @@ sub remote_conn { my $section = $class->lookup_remote($cfg, $remote); + my $trylogin = sub { + my ($ticket_or_password) = @_; + + if (!defined($ticket_or_password)) { + $ticket_or_password = PVE::APIClient::PTY::read_password("Remote password: ") + } + + my $setup = { + username => $section->{username}, + password => $ticket_or_password, + host => $section->{host}, + port => $section->{port} // 8006, + cached_fingerprints => { + $section->{fingerprint} => 1, + } + }; + + my $conn = PVE::APIClient::LWP->new(%$setup); + + $conn->login(); + + return $conn; + }; + my $password = $section->{password}; - if (!defined($password)) { - $password = PVE::PTY::read_password("Remote password: ") - } - my $conn = PVE::APIClient::LWP->new( - username => $section->{username}, - password => $password, - host => $section->{host}, - port => $section->{port} // 8006, - cached_fingerprints => { - $section->{fingerprint} => 1, + my $conn; + + if (defined($password)) { + $conn = $trylogin->($password); + } else { + + if (my $ticket = PVE::APIClient::Helpers::ticket_cache_lookup($remote)) { + eval { $conn = $trylogin->($ticket); }; + if (my $err = $@) { + PVE::APIClient::Helpers::ticket_cache_update($remote, undef); + if (ref($err) && (ref($err) eq 'PVE::APIClient::Exception') && ($err->{code} == 401)) { + $conn = $trylogin->(); + } else { + die $err; + } + } + } else { + $conn = $trylogin->(); } - ); + } - $conn->login; + PVE::APIClient::Helpers::ticket_cache_update($remote, $conn->{ticket}); return $conn; } +package PVE::APIClient::RemoteConfig; + +use strict; +use warnings; + +use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option); +use PVE::APIClient::SectionConfig; + +use base qw( PVE::APIClient::Config); + +sub type { + return 'remote'; +} + +sub properties { + return { + name => get_standard_option('pveclient-remote-name'), + host => { + description => "The host.", + type => 'string', format => 'address', + optional => 1, + }, + username => { + description => "The username.", + type => 'string', + optional => 1, + }, + password => { + description => "The users password.", + type => 'string', + optional => 1, + }, + port => { + description => "The port.", + type => 'integer', + optional => 1, + default => 8006, + }, + fingerprint => { + description => "Fingerprint.", + type => 'string', + optional => 1, + }, + comment => { + description => "Description.", + type => 'string', + optional => 1, + maxLength => 4096, + }, + }; +} + +sub options { + return { + name => { optional => 0 }, + host => { optional => 0 }, + comment => { optional => 1 }, + username => { optional => 0 }, + password => { optional => 1 }, + port => { optional => 1 }, + fingerprint => { optional => 1 }, + }; +} + +__PACKAGE__->register(); + + +package PVE::APIClient::DefaultsConfig; + +use strict; +use warnings; + +use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option); + +use base qw( PVE::APIClient::Config); + + +sub type { + return 'defaults'; +} + +sub options { + return { + name => { optional => 1 }, + username => { optional => 1 }, + port => { optional => 1 }, + }; +} + __PACKAGE__->register(); -__PACKAGE__->init(); + + +PVE::APIClient::Config->init(); 1;