]> git.proxmox.com Git - pve-client.git/blobdiff - PVE/APIClient/Config.pm
adopt code for changes in pve-common
[pve-client.git] / PVE / APIClient / Config.pm
index 6c5b537d84c3aca9cb17b3ad7517f16bde638b14..5437be656ea1e2588f3d5528b434a0404f4f731e 100644 (file)
@@ -3,135 +3,299 @@ package PVE::APIClient::Config;
 use strict;
 use warnings;
 use JSON;
+use File::Basename qw(dirname);
+use File::Path qw(make_path);
 
-use File::HomeDir ();
-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);
 
-sub new {
-    my ($class) = @_;
+use base qw(PVE::APIClient::SectionConfig);
 
-    my $self = {
-       file         => File::HomeDir::home() . '/.pveclient',
-    };
-    bless $self => $class;
+my $remote_namne_regex = qw(\w+);
+
+my $defaults_section = '!DEFAULTS';
 
-    $self->load();
+my $complete_remote_name = sub {
 
-    return $self;
+    my $config = PVE::APIClient::Config->load();
+    my $list = [];
+    foreach my $name (keys %{$config->{ids}}) {
+       push @$list, $name if $name ne $defaults_section;
+    }
+    return $list;
+};
+
+PVE::APIClient::JSONSchema::register_standard_option('pveclient-remote-name', {
+    description => "The name of the remote.",
+    type => 'string',
+    pattern => $remote_namne_regex,
+    completion => $complete_remote_name,
+});
+
+my $defaultData = {
+    propertyList => {
+       type => {
+           description => "Section type.",
+           optional => 1,
+       },
+    },
+};
+
+sub private {
+    return $defaultData;
 }
 
-sub load {
-    my ($self) = @_;
+sub config_filename {
+    my ($class) = @_;
 
-    if (-e $self->{file}) {
-       my $filemode = (stat($self->{file}))[2] & 07777;
-       if ($filemode != 0600) {
-           die sprintf "wrong permissions on '$self->{file}' %04o (expected 0600)\n", $filemode;
-       }
+    my $dir = PVE::APIClient::Helpers::configuration_directory();
+
+    return "$dir/config";
+}
 
-       my $contents = file_get_contents($self->{file});
-       $self->{data} = from_json($contents);
+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;
+}
+
+sub format_section_header {
+    my ($class, $type, $sectionId, $scfg, $done_hash) = @_;
+
+    if ($type eq 'defaults') {
+       return "defaults:\n";
     } else {
-       $self->{data} = {};
+       return "$type: $sectionId\n";
     }
+}
 
-    if (!exists($self->{data}->{remotes})) {
-       $self->{data}->{remotes} = {};
+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 {
+    my ($class) = @_;
+
+    my $filename = $class->config_filename();
 
-    # Verify config
-    for my $name (@{$self->remote_names}) {
-       my $cfg = $self->{data}->{remotes}->{$name};
+    my $raw = '';
 
-       foreach my $opt (qw(host port username fingerprint)) {
-         die "missing option '$opt' (remote '$name')" if !defined($cfg->{$opt});
+    if (-e $filename) {
+       my $filemode = (stat($filename))[2] & 07777;
+       if ($filemode != 0600) {
+           die sprintf "wrong permissions on '$filename' %04o (expected 0600)\n", $filemode;
        }
+
+       $raw = file_get_contents($filename);
     }
+
+    return $class->parse_config($filename, $raw);
 }
 
 sub save {
-    my ($self) = @_;
+    my ($class, $cfg) = @_;
 
-    my $contents = to_json($self->{data}, {pretty => 1, canonical => 1});
-    file_set_contents($self->{file}, $contents, 0600);
-}
+    my $filename = $class->config_filename();
 
-sub add_remote {
-    my ($self, $name, $host, $port, $fingerprint, $username, $password) = @_;
+    make_path(dirname($filename));
 
-    $self->{data}->{remotes}->{$name} = {
-       host => $host,
-       port => $port,
-       fingerprint => $fingerprint,
-       username => $username,
-    };
+    $cfg->{order}->{$defaults_section} = -1; # write as first section
+    my $raw = $class->write_config($filename, $cfg);
 
-    if (defined($password)) {
-       $self->{data}->{remotes}->{$name}->{password} = $password;
-    }
+    file_set_contents($filename, $raw, 0600);
 }
 
-sub remote_names {
-    my ($self) = @_;
+sub get_defaults {
+    my ($class, $cfg) = @_;
+
+    $cfg->{ids}->{$defaults_section} //= {};
 
-    return [keys %{$self->{data}->{remotes}}];
+    return $cfg->{ids}->{$defaults_section};
 }
 
 sub lookup_remote {
-    my ($self, $name) = @_;
+    my ($class, $cfg, $name, $noerr) = @_;
 
-    die "Unknown remote \"$name\" given"
-      if (!exists($self->{data}->{remotes}->{$name}));
+    my $data = $cfg->{ids}->{$name};
 
-    return $self->{data}->{remotes}->{$name};
+    return $data if $noerr || defined($data);
+
+    die "unknown remote \"$name\"\n";
 }
 
-sub remotes {
-    my ($self) = @_;
+sub remote_conn {
+    my ($class, $cfg, $remote) = @_;
+
+    my $section = $class->lookup_remote($cfg, $remote);
 
-    my $res = {};
+    my $trylogin = sub {
+       my ($ticket_or_password) = @_;
 
-    # Remove the password from each remote.
-    for my $name ($self->remote_names) {
-       my $cfg = $self->{data}->{remotes}->{$name};
-       $res->{$name} = {
-           host        => $cfg->{host},
-           port        => $cfg->{port},
-           username    => $cfg->{username},
-           fingerprint => $cfg->{fingerprint},
+       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};
+
+    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->();
+       }
     }
 
-    return $res;
+    PVE::APIClient::Helpers::ticket_cache_update($remote, $conn->{ticket});
+
+    return $conn;
 }
 
-sub remove_remote {
-    my ($self, $remote) = @_;
+package PVE::APIClient::RemoteConfig;
 
-    $self->lookup_remote($remote);
+use strict;
+use warnings;
 
-    delete($self->{data}->{remotes}->{$remote});
+use PVE::APIClient::JSONSchema qw(register_standard_option get_standard_option);
+use PVE::APIClient::SectionConfig;
 
-    $self->save();
+use base qw( PVE::APIClient::Config);
+
+sub type {
+    return 'remote';
 }
 
-sub remote_conn {
-    my ($self, $remote) = @_;
-
-    my $section = $self->lookup_remote($remote);
-    my $conn = PVE::APIClient::LWP->new(
-       username                => $section->{username},
-       password                => $section->{password},
-       host                    => $section->{host},
-       port                    => $section->{port},
-       cached_fingerprints     => {
-           $section->{fingerprint} => 1,
-       }
-    );
+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,
+       },
+    };
+}
 
-    $conn->login;
+sub options {
+    return {
+       name => { optional => 0 },
+       host => { optional => 0 },
+       comment => { optional => 1 },
+       username => { optional => 0 },
+       password => { optional => 1 },
+       port => { optional => 1 },
+       fingerprint => { optional => 1 },
+   };
+}
 
-    return $conn;
+__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();
+
+
+PVE::APIClient::Config->init();
+
 1;