]>
git.proxmox.com Git - pve-common.git/blob - src/PVE/PBSClient.pm
1d5d111fc5543ce1c73236b832fb8d53d99f3979
1 package PVE
::PBSClient
;
2 # utility functions for interaction with Proxmox Backup client CLI executable
7 use Fcntl
qw(F_GETFD F_SETFD FD_CLOEXEC);
10 use POSIX
qw(strftime ENOENT);
12 use PVE
::JSONSchema
qw(get_standard_option);
13 use PVE
::Tools
qw(run_command file_set_contents file_get_contents file_read_firstline);
16 my ($class, $scfg, $storeid, $sdir) = @_;
18 die "no section config provided\n" if ref($scfg) eq '';
19 die "undefined store id\n" if !defined($storeid);
21 my $secret_dir = $sdir // '/etc/pve/priv/storage';
26 secret_dir
=> $secret_dir
31 my sub password_file_name
{
34 return "$self->{secret_dir}/$self->{storeid}.pw";
38 my ($self, $password) = @_;
40 my $pwfile = $self->password_file_name();
41 mkdir $self->{secret_dir
};
43 PVE
::Tools
::file_set_contents
($pwfile, "$password\n", 0600);
49 my $pwfile = $self->password_file_name();
57 my $pwfile = $self->password_file_name();
59 return PVE
::Tools
::file_read_firstline
($pwfile);
62 sub encryption_key_file_name
{
65 return "$self->{secret_dir}/$self->{storeid}.enc";
68 sub set_encryption_key
{
69 my ($self, $key) = @_;
71 my $encfile = $self->encryption_key_file_name();
72 mkdir $self->{secret_dir
};
74 PVE
::Tools
::file_set_contents
($encfile, "$key\n", 0600);
77 sub delete_encryption_key
{
80 my $encfile = $self->encryption_key_file_name();
82 if (!unlink $encfile) {
83 return if $! == ENOENT
;
84 die "failed to delete encryption key! $!\n";
88 # Returns a file handle if there is an encryption key, or `undef` if there is not. Dies on error.
89 my sub open_encryption_key
{
92 my $encryption_key_file = $self->encryption_key_file_name();
95 if (!open($keyfd, '<', $encryption_key_file)) {
96 return undef if $! == ENOENT
;
97 die "failed to open encryption key: $encryption_key_file: $!\n";
103 my $USE_CRYPT_PARAMS = {
109 my sub do_raw_client_cmd
{
110 my ($self, $client_cmd, $param, %opts) = @_;
112 my $use_crypto = $USE_CRYPT_PARAMS->{$client_cmd};
114 my $client_exe = '/usr/bin/proxmox-backup-client';
115 die "executable not found '$client_exe'! Proxmox backup client not installed?\n"
118 my $scfg = $self->{scfg
};
119 my $server = $scfg->{server
};
120 my $datastore = $scfg->{datastore
};
121 my $username = $scfg->{username
} // 'root@pam';
123 my $userns_cmd = delete $opts{userns_cmd
};
127 push @$cmd, @$userns_cmd if defined($userns_cmd);
129 push @$cmd, $client_exe, $client_cmd;
131 # This must live in the top scope to not get closed before the `run_command`
134 if (defined($keyfd = $self->open_encryption_key())) {
135 my $flags = fcntl($keyfd, F_GETFD
, 0)
136 // die "failed to get file descriptor flags: $!\n";
137 fcntl($keyfd, F_SETFD
, $flags & ~FD_CLOEXEC
)
138 or die "failed to remove FD_CLOEXEC from encryption key file descriptor\n";
139 push @$cmd, '--crypt-mode=encrypt', '--keyfd='.fileno($keyfd);
141 push @$cmd, '--crypt-mode=none';
145 push @$cmd, @$param if defined($param);
147 push @$cmd, "--repository", "$username\@$server:$datastore";
149 local $ENV{PBS_PASSWORD
} = $self->get_password();
151 local $ENV{PBS_FINGERPRINT
} = $scfg->{fingerprint
};
153 # no ascii-art on task logs
154 local $ENV{PROXMOX_OUTPUT_NO_BORDER
} = 1;
155 local $ENV{PROXMOX_OUTPUT_NO_HEADER
} = 1;
157 if (my $logfunc = $opts{logfunc
}) {
158 $logfunc->("run: " . join(' ', @$cmd));
161 run_command
($cmd, %opts);
164 my sub run_raw_client_cmd
{
165 my ($self, $client_cmd, $param, %opts) = @_;
166 return $self->do_raw_client_cmd($client_cmd, $param, %opts);
169 my sub run_client_cmd
{
170 my ($self, $client_cmd, $param, $no_output) = @_;
173 my $outfunc = sub { $json_str .= "$_[0]\n" };
175 $param = [] if !defined($param);
176 $param = [ $param ] if !ref($param);
178 $param = [@$param, '--output-format=json'] if !$no_output;
180 $self->do_raw_client_cmd(
184 errmsg
=> 'proxmox-backup-client failed'
187 return undef if $no_output;
189 my $res = decode_json
($json_str);
194 sub autogen_encryption_key
{
196 my $encfile = $self->encryption_key_file_name();
198 ['proxmox-backup-client', 'key', 'create', '--kdf', 'none', $encfile],
199 errmsg
=> 'failed to create encryption key'
201 return file_get_contents
($encfile);
205 my ($self, $opts) = @_;
208 push @$param, $opts->{group
} if defined($opts->{group
});
210 return $self->run_client_cmd("snapshots", $param);
214 my ($self, $opts) = @_;
216 my $type = delete $opts->{type
};
217 die "backup-type not provided\n" if !defined($type);
218 my $id = delete $opts->{id
};
219 die "backup-id not provided\n" if !defined($id);
220 my $root = delete $opts->{root
};
221 die "root dir not provided\n" if !defined($root);
222 my $pxarname = delete $opts->{pxarname
};
223 die "archive name not provided\n" if !defined($pxarname);
224 my $time = delete $opts->{time};
227 "$pxarname.pxar:$root",
228 '--backup-type', $type,
231 push @$param, '--backup-time', $time if defined($time);
233 return $self->run_raw_client_cmd('backup', $param, %$opts);
237 my ($self, $snapshot, $pxarname, $target, $cmd_opts) = @_;
239 die "snapshot not provided\n" if !defined($snapshot);
240 die "archive name not provided\n" if !defined($pxarname);
241 die "restore-target not provided\n" if !defined($target);
247 "--allow-existing-dirs", 0,
251 return $self->run_raw_client_cmd('restore', $param, %$cmd_opts);
254 sub forget_snapshot
{
255 my ($self, $snapshot) = @_;
257 die "snapshot not provided\n" if !defined($snapshot);
259 return $self->run_raw_client_cmd('forget', ["$snapshot"]);
263 my ($self, $opts, $prune_opts, $group) = @_;
265 die "group not provided\n" if !defined($group);
267 # do nothing if no keep options specified for remote
268 return [] if scalar(keys %$prune_opts) == 0;
272 push @$param, "--quiet";
274 if (defined($opts->{'dry-run'}) && $opts->{'dry-run'}) {
275 push @$param, "--dry-run", $opts->{'dry-run'};
278 foreach my $keep_opt (keys %$prune_opts) {
279 push @$param, "--$keep_opt", $prune_opts->{$keep_opt};
281 push @$param, "$group";
283 return $self->run_client_cmd('prune', $param);
295 my $res = $self->run_client_cmd("status");
298 $total = $res->{total
};
299 $used = $res->{used
};
300 $free = $res->{avail
};
306 return ($total, $free, $used, $active);