Compare commits

..

27 Commits

Author SHA1 Message Date
97cd0a2a6d bump version to 0.8.13-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-27 16:15:31 +02:00
49a92084a9 gc: use human readable units for summary
and avoid the "percentage done: X %" phrase

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-27 16:06:35 +02:00
9bdeecaee4 bump pxar dep to 0.6.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-08-27 12:16:21 +02:00
843880f008 bin/backup-proxy: assert that daemon runs as backup user/group
Because if not, the backups it creates have bogus permissions and may
seem like they got broken once the daemon is started again with the
correct user/group.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-26 10:30:15 +02:00
a6ed5e1273 backup: add BACKUP_GROUP_NAME const and backup_group helper
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-26 10:27:47 +02:00
74f94d0678 bin/backup-proxy: remove outdated perl comments
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-26 10:27:47 +02:00
946c3e8a81 bin/backup-proxy: return error directly in main
anyhow makes this a nice error message, similar to the manual
wrapping used.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-26 10:27:47 +02:00
7b212c1f79 ui: datastore content: show last verify result from a snapshot
Double-click on the verify grid-cell of a specific snapshot (not the
group) opens the relevant task log.

The date of the last verify is shown as tool-tip.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-26 07:36:16 +02:00
3b2046d263 save last verify result in snapshot manifest
Save the state ("ok" or "failed") and the UPID of the respective
verify task. With this we can easily allow to open the relevant task
log and show when the last verify happened.

As we already load the manifest when listing the snapshots, just add
it there directly.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-26 07:35:13 +02:00
1ffe030123 various typo fixes
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-25 18:52:31 +02:00
5255e641fa SnapshotListItem: add comment field also to schema
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-25 16:24:36 +02:00
c86b6f40d7 tools/format: implement from u64 for HumanByte helper type
Could be problematic for systems where usize is 32 bit, but we do not
really support those.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-25 14:18:49 +02:00
5a718dce17 api datastore: fix typo in error message
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-25 14:16:40 +02:00
1b32750644 update d/control for pxar 0.5.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-08-25 12:37:11 +02:00
5aa103c3c3 bump pxar dep to 0.5.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-08-25 12:37:11 +02:00
fd3f690104 Add section "Garbage Collection"
Add the section "Garbage Collection" to section "Backup Server
Management". This briefly explains the "garbage-collection"
subcommand of "proxmox-backup-manager"

Signed-off-by: Dylan Whyte <d.whyte@proxmox.com>
2020-08-25 09:38:03 +02:00
24b638bd9f Add section "Network Management"
Add the section "Network Management", which explains the
"network" subcommand of "proxmox-backup-manager"

Signed-off-by: Dylan Whyte <d.whyte@proxmox.com>
2020-08-25 09:37:41 +02:00
9624c5eecb add note about TLS benchmark test. 2020-08-25 09:36:12 +02:00
503dd339a8 Add further explanation to benchmarking
Adds a note, explaing the percentages shown in the output
of the benchmark

Signed-off-by: Dylan Whyte <d.whyte@proxmox.com>
2020-08-25 09:33:23 +02:00
36ea5df444 administration-guide.rst: remove debug output from code examples 2020-08-25 09:29:52 +02:00
dce9dd6f70 Add section "Disk Management"
Add the section "Disk Management" to the admin guide, explaining
the use of the "disk" subcommand of "proxmox-backup-manager"

Signed-off-by: Dylan Whyte <d.whyte@proxmox.com>
2020-08-25 09:27:48 +02:00
88e28e15e4 debian/control: update for new pxar 0.4 dependency 2020-08-25 09:09:37 +02:00
399e48a1ed bump version to 0.8.12-1 2020-08-25 08:57:12 +02:00
7ae571e7cb verify: speedup - only verify chunks once
We need to do the check before we load the chunk.
2020-08-25 08:52:24 +02:00
4264c5023b verify: sort backup groups 2020-08-25 08:38:47 +02:00
82b7adf90b bump pxar dep to 0.4.0
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2020-08-24 11:56:01 +02:00
71c4a3138f docs: fix PBS wiki link
rst/sphinx and comments are a PITA...

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-08-21 11:09:41 +02:00
22 changed files with 368 additions and 59 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "proxmox-backup"
version = "0.8.11"
version = "0.8.13"
authors = ["Dietmar Maurer <dietmar@proxmox.com>"]
edition = "2018"
license = "AGPL-3"
@ -43,7 +43,7 @@ proxmox = { version = "0.3.3", features = [ "sortable-macro", "api-macro", "webs
#proxmox = { git = "ssh://gitolite3@proxdev.maurer-it.com/rust/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] }
#proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro", "websocket" ] }
proxmox-fuse = "0.1.0"
pxar = { version = "0.3.0", features = [ "tokio-io", "futures-io" ] }
pxar = { version = "0.6.0", features = [ "tokio-io", "futures-io" ] }
#pxar = { path = "../pxar", features = [ "tokio-io", "futures-io" ] }
regex = "1.2"
rustyline = "6"

20
debian/changelog vendored
View File

@ -1,3 +1,23 @@
rust-proxmox-backup (0.8.13-1) unstable; urgency=medium
* improve and add to documentation
* save last verify result in snapshot manifest and show it in the GUI
* gc: use human readable units for summary in task log
-- Proxmox Support Team <support@proxmox.com> Thu, 27 Aug 2020 16:12:07 +0200
rust-proxmox-backup (0.8.12-1) unstable; urgency=medium
* verify: speedup - only verify chunks once
* verify: sort backup groups
* bump pxar dep to 0.4.0
-- Proxmox Support Team <support@proxmox.com> Tue, 25 Aug 2020 08:55:52 +0200
rust-proxmox-backup (0.8.11-1) unstable; urgency=medium
* improve sync jobs, allow to stop them and better logging

6
debian/control vendored
View File

@ -39,9 +39,9 @@ Build-Depends: debhelper (>= 11),
librust-proxmox-0.3+sortable-macro-dev (>= 0.3.3-~~),
librust-proxmox-0.3+websocket-dev (>= 0.3.3-~~),
librust-proxmox-fuse-0.1+default-dev,
librust-pxar-0.3+default-dev,
librust-pxar-0.3+futures-io-dev,
librust-pxar-0.3+tokio-io-dev,
librust-pxar-0.6+default-dev,
librust-pxar-0.6+futures-io-dev,
librust-pxar-0.6+tokio-io-dev,
librust-regex-1+default-dev (>= 1.2-~~),
librust-rustyline-6+default-dev,
librust-serde-1+default-dev,

View File

@ -146,6 +146,74 @@ when setting up the backup server.
filesystem configuration from being supported for a datastore. For example,
``ext3`` as a whole or ``ext4`` with the ``dir_nlink`` feature manually disabled.
Disk Management
~~~~~~~~~~~~~~~
Proxmox Backup Server comes with a set of disk utilities, which are
accessed using the ``disk`` subcommand. This subcommand allows you to initialize
disks, create various filesystems, and get information about the disks.
To view the disks connected to the system, use the ``list`` subcommand of
``disk``:
.. code-block:: console
# proxmox-backup-manager disk list
┌──────┬────────┬─────┬───────────┬─────────────┬───────────────┬─────────┬────────┐
│ name │ used │ gpt │ disk-type │ size │ model │ wearout │ status │
╞══════╪════════╪═════╪═══════════╪═════════════╪═══════════════╪═════════╪════════╡
│ sda │ lvm │ 1 │ hdd │ 34359738368 │ QEMU_HARDDISK │ - │ passed │
├──────┼────────┼─────┼───────────┼─────────────┼───────────────┼─────────┼────────┤
│ sdb │ unused │ 1 │ hdd │ 68719476736 │ QEMU_HARDDISK │ - │ passed │
├──────┼────────┼─────┼───────────┼─────────────┼───────────────┼─────────┼────────┤
│ sdc │ unused │ 1 │ hdd │ 68719476736 │ QEMU_HARDDISK │ - │ passed │
└──────┴────────┴─────┴───────────┴─────────────┴───────────────┴─────────┴────────┘
To initialize a disk with a new GPT, use the ``initialize`` subcommand:
.. code-block:: console
# proxmox-backup-manager disk initialize sdX
You can create an ``ext4`` or ``xfs`` filesystem on a disk, using ``fs
create``. The following command creates an ``ext4`` filesystem and passes the
``--add-datastore`` parameter, in order to automatically create a datastore on
the disk (in this case ``sdd``). This will create a datastore at the location
``/mnt/datastore/store1``:
.. code-block:: console
# proxmox-backup-manager disk fs create store1 --disk sdd --filesystem ext4 --add-datastore true
create datastore 'store1' on disk sdd
Percentage done: 1
...
Percentage done: 99
TASK OK
You can also create a ``zpool`` with various raid levels. The command below
creates a mirrored ``zpool`` using two disks (``sdb`` & ``sdc``) and mounts it
on the root directory (default):
.. code-block:: console
# proxmox-backup-manager disk zpool create zpool1 --devices sdb,sdc --raidlevel mirror
create Mirror zpool 'zpool1' on devices 'sdb,sdc'
# "zpool" "create" "-o" "ashift=12" "zpool1" "mirror" "sdb" "sdc"
TASK OK
.. note::
You can also pass the ``--add-datastore`` parameter here, to automatically
create a datastore from the disk.
You can use ``disk fs list`` and ``disk zpool list`` to keep track of your
filesystems and zpools respectively.
If a disk supports S.M.A.R.T. capability, and you have this enabled, you can
display S.M.A.R.T. attributes using the command:
.. code-block:: console
# proxmox-backup-manager disk smart-attributes sdX
Datastore Configuration
~~~~~~~~~~~~~~~~~~~~~~~
@ -404,6 +472,72 @@ A single user can be assigned multiple permission sets for different data stores
remote (see `Remote` below) and ``{storename}`` is the name of the data store on
the remote.
Network Management
~~~~~~~~~~~~~~~~~~
Proxmox Backup Server provides an interface for network configuration, through the
``network`` subcommand. This allows you to carry out some basic network
management tasks such as adding, configuring and removing network interfaces.
To get a list of available interfaces, use the following command:
.. code-block:: console
# proxmox-backup-manager network list
┌───────┬────────┬───────────┬────────┬─────────┬───────────────────┬──────────────┬──────────────┐
│ name │ type │ autostart │ method │ method6 │ address │ gateway │ ports/slaves │
╞═══════╪════════╪═══════════╪════════╪═════════╪═══════════════════╪══════════════╪══════════════╡
│ bond0 │ bond │ 1 │ manual │ │ │ │ ens18 ens19 │
├───────┼────────┼───────────┼────────┼─────────┼───────────────────┼──────────────┼──────────────┤
│ ens18 │ eth │ 1 │ manual │ │ │ │ │
├───────┼────────┼───────────┼────────┼─────────┼───────────────────┼──────────────┼──────────────┤
│ ens19 │ eth │ 1 │ manual │ │ │ │ │
├───────┼────────┼───────────┼────────┼─────────┼───────────────────┼──────────────┼──────────────┤
│ vmbr0 │ bridge │ 1 │ static │ │ x.x.x.x/x │ x.x.x.x │ bond0 │
└───────┴────────┴───────────┴────────┴─────────┴───────────────────┴──────────────┴──────────────┘
To add a new network interface, use the ``create`` subcommand with the relevant
parameters. The following command shows a template for creating a new bridge:
.. code-block:: console
# proxmox-backup-manager network create vmbr1 --autostart true --cidr x.x.x.x/x --gateway x.x.x.x --bridge_ports iface_name --type bridge
You can make changes to the configuration of a network interface with the
``update`` subcommand:
.. code-block:: console
# proxmox-backup-manager network update vmbr1 --cidr y.y.y.y/y
You can also remove a network interface:
.. code-block:: console
# proxmox-backup-manager network remove vmbr1
To view the changes made to the network configuration file, before committing
them, use the command:
.. code-block:: console
# proxmox-backup-manager network changes
If you would like to cancel all changes at this point, you can do this using:
.. code-block:: console
# proxmox-backup-manager network revert
If you are happy with the changes and would like to write them into the
configuration file, the command is:
.. code-block:: console
# proxmox-backup-manager network reload
You can also configure DNS settings using the ``dns`` subcommand of
``proxmox-backup-manager``.
:term:`Remote`
~~~~~~~~~~~~~~
@ -461,6 +595,14 @@ provide it with a :term:`schedule` to run regularly. The
└────────────┴───────┴────────┴──────────────┴───────────┴─────────┘
# proxmox-backup-manager sync-job remove pbs2-local
Garbage Collection
~~~~~~~~~~~~~~~~~~
You can monitor and run :ref:`garbage collection <garbage-collection>` on the
Proxmox Backup Server using the ``garbage-collection`` subcommand of
``proxmox-backup-manager``. You can use the ``start`` subcommand to manually start garbage
collection on an entire data store and the ``status`` subcommand to see
attributes relating to the :ref:`garbage collection <garbage-collection>`.
Backup Client usage
-------------------
@ -1143,6 +1285,10 @@ benchmark using the ``benchmark`` subcommand of ``proxmox-backup-client``:
│ AES256 GCM encryption speed │ 3974.03 MB/s (104%) │
└───────────────────────────────────┴─────────────────────┘
.. note:: The percentages given in the output table correspond to a
comparison against a Ryzen 7 2700X. The TLS test connects to the
local host, so there is no network involved.
You can also pass the ``--output-format`` parameter to output stats in ``json``,
rather than the default table format.

View File

@ -13,7 +13,8 @@
.. _Proxmox: https://www.proxmox.com
.. _Proxmox Community Forum: https://forum.proxmox.com
.. _Proxmox Virtual Environment: https://www.proxmox.com/proxmox-ve
.. _Proxmox Backup: https://pbs.proxmox.com/wiki/index.php/Main_Page // FIXME
// FIXME
.. _Proxmox Backup: https://pbs.proxmox.com/wiki/index.php/Main_Page
.. _PBS Development List: https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
.. _reStructuredText: https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html
.. _Rust: https://www.rust-lang.org/

View File

@ -361,7 +361,7 @@ pub fn list_snapshots (
let mut size = None;
let (comment, files) = match get_all_snapshot_files(&datastore, &info) {
let (comment, verification, files) = match get_all_snapshot_files(&datastore, &info) {
Ok((manifest, files)) => {
size = Some(files.iter().map(|x| x.size.unwrap_or(0)).sum());
// extract the first line from notes
@ -370,11 +370,21 @@ pub fn list_snapshots (
.and_then(|notes| notes.lines().next())
.map(String::from);
(comment, files)
let verify = manifest.unprotected["verify_state"].clone();
let verify: Option<SnapshotVerifyState> = match serde_json::from_value(verify) {
Ok(verify) => verify,
Err(err) => {
eprintln!("error parsing verification state : '{}'", err);
None
}
};
(comment, verify, files)
},
Err(err) => {
eprintln!("error during snapshot file listing: '{}'", err);
(
None,
None,
info
.files
@ -394,6 +404,7 @@ pub fn list_snapshots (
backup_id: group.backup_id().to_string(),
backup_time: info.backup_dir.backup_time().timestamp(),
comment,
verification,
files,
size,
owner: Some(owner),
@ -489,7 +500,7 @@ pub fn verify(
(None, None, None) => {
worker_id = store.clone();
}
_ => bail!("parameters do not spefify a backup group or snapshot"),
_ => bail!("parameters do not specify a backup group or snapshot"),
}
let userid: Userid = rpcenv.get_user().unwrap().parse()?;
@ -519,7 +530,7 @@ pub fn verify(
for dir in failed_dirs {
worker.log(format!("\t{}", dir));
}
bail!("verfication failed - please check the log for details");
bail!("verification failed - please check the log for details");
}
Ok(())
},
@ -1218,7 +1229,7 @@ fn catalog(
pub const API_METHOD_PXAR_FILE_DOWNLOAD: ApiMethod = ApiMethod::new(
&ApiHandler::AsyncHttp(&pxar_file_download),
&ObjectSchema::new(
"Download single file from pxar file of a bacup snapshot. Only works if it's not encrypted.",
"Download single file from pxar file of a backup snapshot. Only works if it's not encrypted.",
&sorted!([
("store", false, &DATASTORE_SCHEMA),
("backup-type", false, &BACKUP_TYPE_SCHEMA),

View File

@ -6,6 +6,7 @@ use proxmox::const_regex;
use proxmox::{IPRE, IPV4RE, IPV6RE, IPV4OCTET, IPV6H16, IPV6LS32};
use crate::backup::CryptMode;
use crate::server::UPID;
#[macro_use]
mod macros;
@ -379,6 +380,25 @@ pub struct GroupListItem {
pub owner: Option<Userid>,
}
#[api(
properties: {
upid: {
schema: UPID_SCHEMA
},
state: {
type: String
},
},
)]
#[derive(Serialize, Deserialize)]
/// Task properties.
pub struct SnapshotVerifyState {
/// UPID of the verify task
pub upid: UPID,
/// State of the verification. "failed" or "ok"
pub state: String,
}
#[api(
properties: {
"backup-type": {
@ -390,6 +410,14 @@ pub struct GroupListItem {
"backup-time": {
schema: BACKUP_TIME_SCHEMA,
},
comment: {
schema: SINGLE_LINE_COMMENT_SCHEMA,
optional: true,
},
verification: {
type: SnapshotVerifyState,
optional: true,
},
files: {
items: {
schema: BACKUP_ARCHIVE_NAME_SCHEMA
@ -411,6 +439,9 @@ pub struct SnapshotListItem {
/// The first line from manifest "notes"
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<String>,
/// The result of the last run verify task
#[serde(skip_serializing_if="Option::is_none")]
pub verification: Option<SnapshotVerifyState>,
/// List of contained archive files.
pub files: Vec<BackupContent>,
/// Overall snapshot size (sum of all archive sizes).

View File

@ -9,7 +9,7 @@
//! with `String`, meaning you can only make references to it.
//! * [`Realm`]: an owned realm (`String` equivalent).
//! * [`RealmRef`]: a borrowed realm (`str` equivalent).
//! * [`Userid`]: an owned user id (`"user@realm"`). Note that this does not have a separte
//! * [`Userid`]: an owned user id (`"user@realm"`). Note that this does not have a separate
//! borrowed type.
//!
//! Note that `Username`s are not unique, therefore they do not implement `Eq` and cannot be

View File

@ -120,6 +120,8 @@ macro_rules! PROXMOX_BACKUP_READER_PROTOCOL_ID_V1 {
/// Unix system user used by proxmox-backup-proxy
pub const BACKUP_USER_NAME: &str = "backup";
/// Unix system group used by proxmox-backup-proxy
pub const BACKUP_GROUP_NAME: &str = "backup";
/// Return User info for the 'backup' user (``getpwnam_r(3)``)
pub fn backup_user() -> Result<nix::unistd::User, Error> {
@ -129,6 +131,14 @@ pub fn backup_user() -> Result<nix::unistd::User, Error> {
}
}
/// Return Group info for the 'backup' group (``getgrnam(3)``)
pub fn backup_group() -> Result<nix::unistd::Group, Error> {
match nix::unistd::Group::from_name(BACKUP_GROUP_NAME)? {
Some(group) => Ok(group),
None => bail!("Unable to lookup backup user."),
}
}
mod file_formats;
pub use file_formats::*;

View File

@ -45,6 +45,31 @@ pub struct BackupGroup {
backup_id: String,
}
impl std::cmp::Ord for BackupGroup {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let type_order = self.backup_type.cmp(&other.backup_type);
if type_order != std::cmp::Ordering::Equal {
return type_order;
}
// try to compare IDs numerically
let id_self = self.backup_id.parse::<u64>();
let id_other = other.backup_id.parse::<u64>();
match (id_self, id_other) {
(Ok(id_self), Ok(id_other)) => id_self.cmp(&id_other),
(Ok(_), Err(_)) => std::cmp::Ordering::Less,
(Err(_), Ok(_)) => std::cmp::Ordering::Greater,
_ => self.backup_id.cmp(&other.backup_id),
}
}
}
impl std::cmp::PartialOrd for BackupGroup {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl BackupGroup {
pub fn new<T: Into<String>, U: Into<String>>(backup_type: T, backup_id: U) -> Self {

View File

@ -104,7 +104,7 @@ impl ChunkStore {
}
let percentage = (i*100)/(64*1024);
if percentage != last_percentage {
eprintln!("Percentage done: {}", percentage);
eprintln!("{}%", percentage);
last_percentage = percentage;
}
}
@ -295,7 +295,7 @@ impl ChunkStore {
for (entry, percentage) in self.get_chunk_iterator()? {
if last_percentage != percentage {
last_percentage = percentage;
worker.log(format!("percentage done: {}, chunk count: {}", percentage, chunk_count));
worker.log(format!("{}%, processed {} chunks", percentage, chunk_count));
}
worker.fail_on_abort()?;

View File

@ -21,6 +21,7 @@ use super::{DataBlob, ArchiveType, archive_type};
use crate::config::datastore;
use crate::server::WorkerTask;
use crate::tools;
use crate::tools::format::HumanByte;
use crate::tools::fs::{lock_dir_noblock, DirLockGuard};
use crate::api2::types::{GarbageCollectionStatus, Userid};
@ -299,7 +300,7 @@ impl DataStore {
/// And set the owner to 'userid'. If the group already exists, it returns the
/// current owner (instead of setting the owner).
///
/// This also aquires an exclusive lock on the directory and returns the lock guard.
/// This also acquires an exclusive lock on the directory and returns the lock guard.
pub fn create_locked_backup_group(
&self,
backup_group: &BackupGroup,
@ -462,9 +463,8 @@ impl DataStore {
let _exclusive_lock = self.chunk_store.try_exclusive_lock()?;
let now = unsafe { libc::time(std::ptr::null_mut()) };
let oldest_writer = self.chunk_store.oldest_writer().unwrap_or(now);
let phase1_start_time = unsafe { libc::time(std::ptr::null_mut()) };
let oldest_writer = self.chunk_store.oldest_writer().unwrap_or(phase1_start_time);
let mut gc_status = GarbageCollectionStatus::default();
gc_status.upid = Some(worker.to_string());
@ -474,26 +474,26 @@ impl DataStore {
self.mark_used_chunks(&mut gc_status, &worker)?;
worker.log("Start GC phase2 (sweep unused chunks)");
self.chunk_store.sweep_unused_chunks(oldest_writer, now, &mut gc_status, &worker)?;
self.chunk_store.sweep_unused_chunks(oldest_writer, phase1_start_time, &mut gc_status, &worker)?;
worker.log(&format!("Removed bytes: {}", gc_status.removed_bytes));
worker.log(&format!("Removed garbage: {}", HumanByte::from(gc_status.removed_bytes)));
worker.log(&format!("Removed chunks: {}", gc_status.removed_chunks));
if gc_status.pending_bytes > 0 {
worker.log(&format!("Pending removals: {} bytes ({} chunks)", gc_status.pending_bytes, gc_status.pending_chunks));
worker.log(&format!("Pending removals: {} (in {} chunks)", HumanByte::from(gc_status.pending_bytes), gc_status.pending_chunks));
}
worker.log(&format!("Original data bytes: {}", gc_status.index_data_bytes));
worker.log(&format!("Original data usage: {}", HumanByte::from(gc_status.index_data_bytes)));
if gc_status.index_data_bytes > 0 {
let comp_per = (gc_status.disk_bytes*100)/gc_status.index_data_bytes;
worker.log(&format!("Disk bytes: {} ({} %)", gc_status.disk_bytes, comp_per));
let comp_per = (gc_status.disk_bytes as f64 * 100.)/gc_status.index_data_bytes as f64;
worker.log(&format!("On-Disk usage: {} ({:.2}%)", HumanByte::from(gc_status.disk_bytes), comp_per));
}
worker.log(&format!("Disk chunks: {}", gc_status.disk_chunks));
worker.log(&format!("On-Disk chunks: {}", gc_status.disk_chunks));
if gc_status.disk_chunks > 0 {
let avg_chunk = gc_status.disk_bytes/(gc_status.disk_chunks as u64);
worker.log(&format!("Average chunk size: {}", avg_chunk));
worker.log(&format!("Average chunk size: {}", HumanByte::from(avg_chunk)));
}
*self.last_gc_status.lock().unwrap() = gc_status;

View File

@ -145,7 +145,7 @@ impl BackupManifest {
Ok(())
}
// Generate cannonical json
// Generate canonical json
fn to_canonical_json(value: &Value) -> Result<Vec<u8>, Error> {
let mut data = Vec::new();
Self::write_canonical_json(value, &mut data)?;

View File

@ -1,8 +1,9 @@
use std::collections::HashSet;
use anyhow::{bail, Error};
use anyhow::{bail, format_err, Error};
use crate::server::WorkerTask;
use crate::api2::types::*;
use super::{
DataStore, BackupGroup, BackupDir, BackupInfo, IndexFile,
@ -50,7 +51,17 @@ fn verify_index_chunks(
worker.fail_on_abort()?;
let info = index.chunk_info(pos).unwrap();
let size = info.range.end - info.range.start;
if verified_chunks.contains(&info.digest) {
continue; // already verified
}
if corrupt_chunks.contains(&info.digest) {
let digest_str = proxmox::tools::digest_to_hex(&info.digest);
worker.log(format!("chunk {} was marked as corrupt", digest_str));
errors += 1;
continue;
}
let chunk = match datastore.load_chunk(&info.digest) {
Err(err) => {
@ -81,8 +92,8 @@ fn verify_index_chunks(
errors += 1;
}
if !verified_chunks.contains(&info.digest) {
if !corrupt_chunks.contains(&info.digest) {
let size = info.range.end - info.range.start;
if let Err(err) = chunk.verify_unencrypted(size as usize, &info.digest) {
corrupt_chunks.insert(info.digest);
worker.log(format!("{}", err));
@ -90,12 +101,6 @@ fn verify_index_chunks(
} else {
verified_chunks.insert(info.digest);
}
} else {
let digest_str = proxmox::tools::digest_to_hex(&info.digest);
worker.log(format!("chunk {} was marked as corrupt", digest_str));
errors += 1;
}
}
}
if errors > 0 {
@ -174,7 +179,7 @@ pub fn verify_backup_dir(
worker: &WorkerTask
) -> Result<bool, Error> {
let manifest = match datastore.load_manifest(&backup_dir) {
let mut manifest = match datastore.load_manifest(&backup_dir) {
Ok((manifest, _)) => manifest,
Err(err) => {
worker.log(format!("verify {}:{} - manifest load error: {}", datastore.name(), backup_dir, err));
@ -186,6 +191,7 @@ pub fn verify_backup_dir(
let mut error_count = 0;
let mut verify_result = "ok";
for info in manifest.files() {
let result = proxmox::try_block!({
worker.log(format!(" check {}", info.filename));
@ -217,9 +223,20 @@ pub fn verify_backup_dir(
if let Err(err) = result {
worker.log(format!("verify {}:{}/{} failed: {}", datastore.name(), backup_dir, info.filename, err));
error_count += 1;
verify_result = "failed";
}
}
let verify_state = SnapshotVerifyState {
state: verify_result.to_string(),
upid: worker.upid().clone(),
};
manifest.unprotected["verify_state"] = serde_json::to_value(verify_state)?;
datastore.store_manifest(&backup_dir, serde_json::to_value(manifest)?)
.map_err(|err| format_err!("unable to store manifest blob - {}", err))?;
Ok(error_count == 0)
}
@ -267,7 +284,7 @@ pub fn verify_all_backups(datastore: &DataStore, worker: &WorkerTask) -> Result<
let mut errors = Vec::new();
let list = match BackupGroup::list_groups(&datastore.base_path()) {
let mut list = match BackupGroup::list_groups(&datastore.base_path()) {
Ok(list) => list,
Err(err) => {
worker.log(format!("verify datastore {} - unable to list backups: {}", datastore.name(), err));
@ -275,6 +292,8 @@ pub fn verify_all_backups(datastore: &DataStore, worker: &WorkerTask) -> Result<
}
};
list.sort_unstable();
worker.log(format!("verify datastore {}", datastore.name()));
for group in list {

View File

@ -20,13 +20,19 @@ use proxmox_backup::tools::disks::{ DiskManage, zfs_pool_stats };
use proxmox_backup::api2::pull::do_sync_job;
fn main() {
fn main() -> Result<(), Error> {
proxmox_backup::tools::setup_safe_path_env();
if let Err(err) = proxmox_backup::tools::runtime::main(run()) {
eprintln!("Error: {}", err);
std::process::exit(-1);
let backup_uid = proxmox_backup::backup::backup_user()?.uid;
let backup_gid = proxmox_backup::backup::backup_group()?.gid;
let running_uid = nix::unistd::Uid::effective();
let running_gid = nix::unistd::Gid::effective();
if running_uid != backup_uid || running_gid != backup_gid {
bail!("proxy not running as backup user or group (got uid {} gid {})", running_uid, running_gid);
}
proxmox_backup::tools::runtime::main(run())
}
async fn run() -> Result<(), Error> {
@ -43,11 +49,6 @@ async fn run() -> Result<(), Error> {
let mut config = ApiConfig::new(
buildcfg::JS_DIR, &proxmox_backup::api2::ROUTER, RpcEnvironmentType::PUBLIC)?;
// add default dirs which includes jquery and bootstrap
// my $base = '/usr/share/libpve-http-server-perl';
// add_dirs($self->{dirs}, '/css/' => "$base/css/");
// add_dirs($self->{dirs}, '/js/' => "$base/js/");
// add_dirs($self->{dirs}, '/fonts/' => "$base/fonts/");
config.add_alias("novnc", "/usr/share/novnc-pve");
config.add_alias("extjs", "/usr/share/javascript/extjs");
config.add_alias("fontawesome", "/usr/share/fonts-font-awesome");

View File

@ -239,7 +239,7 @@ pub fn zpool_commands() -> CommandLineInterface {
.insert("create",
CliCommand::new(&API_METHOD_CREATE_ZPOOL)
.arg_param(&["name"])
.completion_cb("devices", complete_disk_name) // fixme: comlete the list
.completion_cb("devices", complete_disk_name) // fixme: complete the list
);
cmd_def.into()

View File

@ -629,7 +629,7 @@ impl BackupWriter {
})
}
/// Upload speed test - prints result ot stderr
/// Upload speed test - prints result to stderr
pub async fn upload_speedtest(&self, verbose: bool) -> Result<f64, Error> {
let mut data = vec![];

View File

@ -133,7 +133,7 @@ impl DiskManage {
})
}
/// Information about file system type and unsed device for a path
/// Information about file system type and used device for a path
///
/// Returns tuple (fs_type, device, mount_source)
pub fn find_mounted_device(

View File

@ -111,7 +111,7 @@ fn parse_zpool_list_item(i: &str) -> IResult<&str, ZFSPoolInfo> {
Ok((i, stat))
}
/// Parse zpool list outout
/// Parse zpool list output
///
/// Note: This does not reveal any details on how the pool uses the devices, because
/// the zpool list output format is not really defined...

View File

@ -53,7 +53,7 @@ fn parse_zpool_status_vdev(i: &str) -> IResult<&str, ZFSPoolVDevState> {
let (i, vdev_name) = notspace1(i)?;
if let Ok((n, _)) = preceded(multispace0, line_ending)(i) { // sepecial device
if let Ok((n, _)) = preceded(multispace0, line_ending)(i) { // special device
let vdev = ZFSPoolVDevState {
name: vdev_name.to_string(),
lvl: indent_level,

View File

@ -80,6 +80,11 @@ impl From<usize> for HumanByte {
HumanByte { b: v }
}
}
impl From<u64> for HumanByte {
fn from(v: u64) -> Self {
HumanByte { b: v as usize }
}
}
#[test]
fn correct_byte_convert() {

View File

@ -10,6 +10,7 @@ Ext.define('pbs-data-store-snapshots', {
},
'files',
'owner',
'verification',
{ name: 'size', type: 'int', allowNull: true, },
{
name: 'crypt-mode',
@ -209,6 +210,10 @@ Ext.define('PBS.DataStoreContent', {
group.size = item.size;
group.owner = item.owner;
}
if (item.verification &&
(!group.verification || group.verification.state !== 'failed')) {
group.verification = item.verification;
}
}
group.count = group.children.length;
@ -563,6 +568,41 @@ Ext.define('PBS.DataStoreContent', {
return (iconTxt + PBS.Utils.cryptText[v]) || Proxmox.Utils.unknownText
}
},
{
header: gettext('Verify State'),
sortable: true,
dataIndex: 'verification',
renderer: (v, meta, record) => {
if (v === undefined || v === null || !v.state) {
//meta.tdCls = "x-grid-row-loading";
return record.data.leaf ? '' : gettext('None');
}
let task = Proxmox.Utils.parse_task_upid(v.upid);
let verify_time = Proxmox.Utils.render_timestamp(task.starttime);
let iconCls = v.state === 'ok' ? 'check good' : 'times critical';
let tip = `Verify task started on ${verify_time}`;
if (record.parentNode.id === 'root') {
tip = v.state === 'ok'
? 'All verification OK in backup group'
: 'At least one failed verification in backup group!';
}
return `<span data-qtip="${tip}">
<i class="fa fa-fw fa-${iconCls}"></i> ${v.state}
</span>`;
},
listeners: {
dblclick: function(view, el, row, col, ev, rec) {
let data = rec.data || {};
let verify = data.verification;
if (verify && verify.upid && rec.parentNode.id !== 'root') {
let win = Ext.create('Proxmox.window.TaskViewer', {
upid: verify.upid,
});
win.show();
}
},
},
},
],
tbar: [