[pve-devel] [PATCH pve-client 5/6] use SectionConfig for PVE::APIClient::Config

Dietmar Maurer dietmar at proxmox.com
Tue Jun 5 12:17:43 CEST 2018


Signed-off-by: Dietmar Maurer <dietmar at proxmox.com>
---
 PVE/APIClient/Commands/lxc.pm    |   3 +-
 PVE/APIClient/Commands/remote.pm | 128 ++++++++++++++++++----------
 PVE/APIClient/Config.pm          | 177 ++++++++++++++++++++-------------------
 3 files changed, 179 insertions(+), 129 deletions(-)

diff --git a/PVE/APIClient/Commands/lxc.pm b/PVE/APIClient/Commands/lxc.pm
index 7403143..250d5f3 100644
--- a/PVE/APIClient/Commands/lxc.pm
+++ b/PVE/APIClient/Commands/lxc.pm
@@ -147,7 +147,8 @@ __PACKAGE__->register_method ({
     code => sub {
 	my ($param) = @_;
 
-	my $conn = PVE::APIClient::Config->new()->remote_conn($param->{remote});
+	my $config = PVE::APIClient::Config->load();
+	my $conn = PVE::APIClient::Config->remote_conn($config, $param->{remote});
 
 	# Get the real node from the resources endpoint
 	my $resource_list = $conn->get("api2/json/cluster/resources", { type => 'vm'});
diff --git a/PVE/APIClient/Commands/remote.pm b/PVE/APIClient/Commands/remote.pm
index 5d04e3a..06bc1b6 100644
--- a/PVE/APIClient/Commands/remote.pm
+++ b/PVE/APIClient/Commands/remote.pm
@@ -4,6 +4,7 @@ use strict;
 use warnings;
 
 use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(extract_param);
 use PVE::APIClient::Config;
 
 use PVE::CLIHandler;
@@ -27,14 +28,13 @@ __PACKAGE__->register_method ({
     },
     returns => { type => 'null' },
     code => sub {
-	my $config = PVE::APIClient::Config->new();
-	my $known_remotes = $config->remote_names;
+	my $config = PVE::APIClient::Config->load();
 
 	printf("%10s %10s %10s %10s %100s\n", "Name", "Host", "Port", "Username", "Fingerprint");
-	for my $name (@$known_remotes) {
-	    my $remote = $config->lookup_remote($name);
+	for my $name (keys %{$config->{ids}}) {
+	    my $remote = $config->{ids}->{$name};
 	    printf("%10s %10s %10s %10s %100s\n", $name, $remote->{'host'},
-		$remote->{'port'}, $remote->{'username'}, $remote->{'fingerprint'});
+		   $remote->{'port'} // '-', $remote->{'username'}, $remote->{'fingerprint'} // '-');
 	}
 
 	return undef;
@@ -45,48 +45,42 @@ __PACKAGE__->register_method ({
     path => 'add',
     method => 'POST',
     description => "Add a remote to your config file.",
-    parameters => {
-	additionalProperties => 0,
-	properties => {
-	    name => get_standard_option('pveclient-remote-name', { completion => sub {} }),
-	    host => {
-		description => "The host.",
-		type => 'string',
-		format => 'address',
-	    },
-	    username => {
-		description => "The username.",
-		type => 'string',
-	    },
-	    password => {
-		description => "The users password",
-		type => 'string',
-	    },
-	    port => {
-		description => "The port",
-		type => 'integer',
-		optional => 1,
-		default => 8006,
-	    }
-	},
-    },
+    parameters => PVE::APIClient::Config->createSchema(1),
     returns => { type => 'null'},
     code => sub {
 	my ($param) = @_;
 
-	my $config = PVE::APIClient::Config->new();
-	my $known_remotes = $config->remotes;
+	# fixme: lock config file
 
-	if (exists($known_remotes->{$param->{name}})) {
-	    die "Remote \"$param->{name}\" exists, remove it first\n";
-	}
+	my $remote = $param->{name};
+
+	my $config = PVE::APIClient::Config->load();
+
+	die "Remote '$remote' already exists\n"
+	    if $config->{ids}->{$remote};
 
 	my $last_fp = 0;
-	my $api = PVE::APIClient::LWP->new(
+
+	my $setup = {
 	    username                => $param->{username},
 	    password                => $param->{password},
 	    host                    => $param->{host},
 	    port                    => $param->{port} // 8006,
+	};
+
+	if ($param->{fingerprint}) {
+	    $setup->{cached_fingerprints} = {
+		$param->{fingerprint} => 1,
+	    };
+	} else {
+	    $setup->{manual_verification} = 1;
+	    $setup->{register_fingerprint_cb} = sub {
+		my $fp = shift @_;
+		$last_fp = $fp;
+	    };
+	}
+
+	my $api = PVE::APIClient::LWP->new(
 	    manual_verification     => 1,
 	    register_fingerprint_cb => sub {
 		my $fp = shift @_;
@@ -95,9 +89,56 @@ __PACKAGE__->register_method ({
 	);
 	$api->login();
 
-	$config->add_remote($param->{name}, $param->{host}, $param->{port} // 8006, 
-			    $last_fp, $param->{username}, $param->{password});
-	$config->save;
+	$param->{fingerprint} = $last_fp if !defined($param->{fingerprint});
+	my $plugin = PVE::APIClient::Config->lookup('remote');
+	my $opts = $plugin->check_config($remote, $param, 1, 1);
+	$config->{ids}->{$remote} = $opts;
+
+	PVE::APIClient::Config->save($config);
+
+	return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    path => 'update',
+    method => 'PUT',
+    description => "Update a remote configuration.",
+    parameters => PVE::APIClient::Config->updateSchema(1),
+    returns => { type => 'null'},
+    code => sub {
+	my ($param) = @_;
+
+	# fixme: lock config file
+
+	my $name = extract_param($param, 'name');
+	my $digest = extract_param($param, 'digest');
+	my $delete = extract_param($param, 'delete');
+
+	my $config = PVE::APIClient::Config->load();
+	my $remote = PVE::APIClient::Config->lookup_remote($config, $name);
+
+	my $plugin = PVE::APIClient::Config->lookup('remote');
+	my $opts = $plugin->check_config($name, $param, 0, 1);
+
+	foreach my $k (%$opts) {
+	    $remote->{$k} = $opts->{$k};
+	}
+
+	if ($delete) {
+	    my $options = $plugin->private()->{options}->{'remote'};
+	    foreach my $k (PVE::Tools::split_list($delete)) {
+		my $d = $options->{$k} ||
+		    die "no such option '$k'\n";
+		die "unable to delete required option '$k'\n"
+		    if !$d->{optional};
+		die "unable to delete fixed option '$k'\n"
+		    if $d->{fixed};
+		delete $remote->{$k};
+	    }
+	}
+
+	PVE::APIClient::Config->save($config);
 
 	return undef;
     }});
@@ -117,15 +158,18 @@ __PACKAGE__->register_method ({
     code => sub {
 	my ($param) = @_;
 
-	my $config = PVE::APIClient::Config->new();
-	$config->remove_remote($param->{name});
-	$config->save;
+	# fixme: lock config
+
+	my $config = PVE::APIClient::Config->load();
+	delete $config->{ids}->{$param->{name}};
+	PVE::APIClient::Config->save($config);
 
 	return undef;
     }});
 
 our $cmddef = {
     add => [ __PACKAGE__, 'add', ['name', 'host', 'username']],
+    update => [ __PACKAGE__, 'update', ['name']],
     remove => [ __PACKAGE__, 'remove', ['name']],
     list => [__PACKAGE__, 'list'],
 };
diff --git a/PVE/APIClient/Config.pm b/PVE/APIClient/Config.pm
index 40caed8..8a77848 100644
--- a/PVE/APIClient/Config.pm
+++ b/PVE/APIClient/Config.pm
@@ -6,12 +6,15 @@ use JSON;
 
 use File::HomeDir ();
 use PVE::JSONSchema qw(register_standard_option get_standard_option);
+use PVE::SectionConfig;
 use PVE::Tools qw(file_get_contents file_set_contents);
 
+use base qw(PVE::SectionConfig);
+
 my $complete_remote_name = sub {
 
-    my $config = PVE::APIClient::Config->new();
-    return $config->remote_names;
+    my $config = PVE::APIClient::Config->load();
+    return [keys %{$config->{ids}}];
 };
 
 register_standard_option('pveclient-remote-name', {
@@ -21,118 +24,117 @@ register_standard_option('pveclient-remote-name', {
     completion => $complete_remote_name,
 });
 
-sub new {
-    my ($class) = @_;
-
-    my $self = {
-	file         => File::HomeDir::home() . '/.pveclient',
-    };
-    bless $self => $class;
 
-    $self->load();
+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,
+	},
+    },
+};
 
-    return $self;
+sub type {
+    return 'remote';
 }
 
-sub load {
-    my ($self) = @_;
-
-    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 $contents = file_get_contents($self->{file});
-	$self->{data} = from_json($contents);
-    } else {
-	$self->{data} = {};
-    }
-
-    if (!exists($self->{data}->{remotes})) {
-	$self->{data}->{remotes} = {};
-    }
-
-    # Verify config
-    for my $name (@{$self->remote_names}) {
-	my $cfg = $self->{data}->{remotes}->{$name};
-
-	foreach my $opt (qw(host port username fingerprint)) {
-	  die "missing option '$opt' (remote '$name')" if !defined($cfg->{$opt});
-	}
-    }
+sub options {
+    return {
+	name => { optional => 0 },
+	host => { optional => 0 },
+	comment => { optional => 1 },
+	username => { optional => 0 },
+	password => { optional => 0 },
+	port => { optional => 1 },
+	fingerprint => { optional => 1 },
+   };
 }
 
-sub save {
-    my ($self) = @_;
-
-    my $contents = to_json($self->{data}, {pretty => 1, canonical => 1});
-    file_set_contents($self->{file}, $contents, 0600);
+sub private {
+    return $defaultData;
 }
 
-sub add_remote {
-    my ($self, $name, $host, $port, $fingerprint, $username, $password) = @_;
-
-    $self->{data}->{remotes}->{$name} = {
-	host => $host,
-	port => $port,
-	fingerprint => $fingerprint,
-	username => $username,
-    };
+sub config_filename {
+    my ($class) = @_;
 
-    if (defined($password)) {
-	$self->{data}->{remotes}->{$name}->{password} = $password;
-    }
+    return File::HomeDir::home() . '/.pveclient';
 }
 
-sub remote_names {
-    my ($self) = @_;
+sub load {
+    my ($class) = @_;
 
-    return [keys %{$self->{data}->{remotes}}];
-}
+    my $filename = $class->config_filename();
 
-sub lookup_remote {
-    my ($self, $name) = @_;
+    my $raw = '';
 
-    die "Unknown remote \"$name\" given"
-      if (!exists($self->{data}->{remotes}->{$name}));
+    if (-e $filename) {
+	my $filemode = (stat($filename))[2] & 07777;
+	if ($filemode != 0600) {
+	    die sprintf "wrong permissions on '$filename' %04o (expected 0600)\n", $filemode;
+	}
 
-    return $self->{data}->{remotes}->{$name};
-}
+	$raw = file_get_contents($filename);
+    }
 
-sub remotes {
-    my ($self) = @_;
+    return $class->parse_config($filename, $raw);
+}
 
-    my $res = {};
+sub save {
+    my ($class, $cfg) = @_;
 
-    # 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},
-	};
-    }
+    my $filename = $class->config_filename();
+    my $raw = $class->write_config($filename, $cfg);
 
-    return $res;
+    file_set_contents($filename, $raw, 0600);
 }
 
-sub remove_remote {
-    my ($self, $remote) = @_;
+sub lookup_remote {
+    my ($class, $cfg, $name, $noerr) = @_;
 
-    $self->lookup_remote($remote);
+    my $data = $cfg->{ids}->{$name};
 
-    delete($self->{data}->{remotes}->{$remote});
+    return $data if $noerr || defined($data);
 
-    $self->save();
+    die "unknown remote \"$name\"\n";
 }
 
 sub remote_conn {
-    my ($self, $remote) = @_;
+    my ($class, $cfg, $remote) = @_;
 
-    my $section = $self->lookup_remote($remote);
+    my $section = $class->lookup_remote($cfg, $remote);
     my $conn = PVE::APIClient::LWP->new(
 	username                => $section->{username},
 	password                => $section->{password},
@@ -148,4 +150,7 @@ sub remote_conn {
     return $conn;
 }
 
+__PACKAGE__->register();
+__PACKAGE__->init();
+
 1;
-- 
2.11.0




More information about the pve-devel mailing list