├── .gitignore ├── ApiChangeLog ├── Makefile ├── debian ├── changelog ├── control ├── copyright ├── docs ├── postinst ├── rules └── triggers └── src ├── Makefile ├── PVE ├── API2 │ ├── Disks.pm │ ├── Disks │ │ ├── Directory.pm │ │ ├── LVM.pm │ │ ├── LVMThin.pm │ │ ├── Makefile │ │ └── ZFS.pm │ ├── Makefile │ └── Storage │ │ ├── Config.pm │ │ ├── Content.pm │ │ ├── FileRestore.pm │ │ ├── Makefile │ │ ├── PruneBackups.pm │ │ ├── Scan.pm │ │ └── Status.pm ├── BackupProvider │ ├── Makefile │ └── Plugin │ │ ├── Base.pm │ │ └── Makefile ├── CLI │ ├── Makefile │ └── pvesm.pm ├── CephConfig.pm ├── Diskmanage.pm ├── GuestImport.pm ├── GuestImport │ ├── Makefile │ └── OVF.pm ├── Makefile ├── Storage.pm ├── Storage │ ├── BTRFSPlugin.pm │ ├── CIFSPlugin.pm │ ├── CephFSPlugin.pm │ ├── Common.pm │ ├── Common │ │ └── Makefile │ ├── DirPlugin.pm │ ├── ESXiPlugin.pm │ ├── GlusterfsPlugin.pm │ ├── ISCSIDirectPlugin.pm │ ├── ISCSIPlugin.pm │ ├── LVMPlugin.pm │ ├── LunCmd │ │ ├── Comstar.pm │ │ ├── Iet.pm │ │ ├── Istgt.pm │ │ ├── LIO.pm │ │ └── Makefile │ ├── LvmThinPlugin.pm │ ├── Makefile │ ├── NFSPlugin.pm │ ├── PBSPlugin.pm │ ├── Plugin.pm │ ├── RBDPlugin.pm │ ├── ZFSPlugin.pm │ └── ZFSPoolPlugin.pm └── test │ ├── Makefile │ └── ceph_conf_parse_write_test.pl ├── bin ├── Makefile └── pvesm ├── test ├── Makefile ├── archive_info_test.pm ├── disk_tests │ ├── cciss │ │ ├── cciss!c0d0 │ │ │ ├── device │ │ │ │ ├── model │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── cciss!c0d0_udevadm │ │ ├── disklist │ │ └── disklist_expected.json │ ├── hdd_smart │ │ ├── disklist │ │ ├── disklist_expected.json │ │ ├── sda │ │ │ ├── device │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sda_health │ │ ├── sda_smart │ │ ├── sda_smart_expected.json │ │ ├── sda_udevadm │ │ ├── sdb │ │ │ ├── device │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sdb_health │ │ ├── sdb_smart │ │ ├── sdb_smart_expected.json │ │ └── sdb_udevadm │ ├── nvme_smart │ │ ├── disklist │ │ ├── disklist_expected.json │ │ ├── nvme0n1 │ │ │ ├── device │ │ │ │ └── model │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── nvme0n1_smart │ │ ├── nvme0n1_smart_expected.json │ │ └── nvme0n1_udevadm │ ├── sas │ │ ├── disklist │ │ ├── disklist_expected.json │ │ ├── sda │ │ │ ├── device │ │ │ │ ├── model │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sda_smart │ │ ├── sda_smart_expected.json │ │ └── sda_udevadm │ ├── sas_ssd │ │ ├── disklist │ │ ├── disklist_expected.json │ │ ├── sda │ │ │ ├── device │ │ │ │ ├── model │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sda_smart │ │ ├── sda_smart_expected.json │ │ └── sda_udevadm │ ├── ssd_smart │ │ ├── disklist │ │ ├── disklist_expected.json │ │ ├── sda │ │ │ ├── device │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sda_smart │ │ ├── sda_smart_expected.json │ │ ├── sda_udevadm │ │ ├── sdb │ │ │ ├── device │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sdb_smart │ │ ├── sdb_smart_expected.json │ │ ├── sdb_udevadm │ │ ├── sdc │ │ │ ├── device │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sdc_smart │ │ ├── sdc_smart_expected.json │ │ ├── sdc_udevadm │ │ ├── sdd │ │ │ ├── device │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sdd_smart │ │ ├── sdd_smart_expected.json │ │ ├── sdd_udevadm │ │ ├── sde │ │ │ ├── device │ │ │ │ └── vendor │ │ │ ├── queue │ │ │ │ └── rotational │ │ │ └── size │ │ ├── sde_smart │ │ ├── sde_smart_expected.json │ │ └── sde_udevadm │ └── usages │ │ ├── disklist │ │ ├── disklist_expected.json │ │ ├── lsblk │ │ ├── lvs │ │ ├── mounts │ │ ├── partlist │ │ ├── pvs │ │ ├── sda │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sda_udevadm │ │ ├── sdb │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdb_udevadm │ │ ├── sdc │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdc_udevadm │ │ ├── sdd │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ ├── sdd1 │ │ │ └── size │ │ ├── sdd2 │ │ │ └── size │ │ └── size │ │ ├── sdd_udevadm │ │ ├── sde │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ ├── sde1 │ │ │ └── size │ │ └── size │ │ ├── sde_udevadm │ │ ├── sdf │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ ├── sdf1 │ │ │ └── size │ │ └── size │ │ ├── sdf_udevadm │ │ ├── sdg │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdg_udevadm │ │ ├── sdh │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdh_udevadm │ │ ├── sdi │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdi_udevadm │ │ ├── sdj │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdj_udevadm │ │ ├── sdk │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdk_udevadm │ │ ├── sdl │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdl_udevadm │ │ ├── sdm │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ ├── sdm1 │ │ │ └── size │ │ ├── sdm9 │ │ │ └── size │ │ └── size │ │ ├── sdm_udevadm │ │ ├── sdn │ │ ├── device │ │ │ └── vendor │ │ ├── queue │ │ │ └── rotational │ │ └── size │ │ ├── sdn_udevadm │ │ └── zpool ├── disklist_test.pm ├── filesystem_path_test.pm ├── get_subdir_test.pm ├── list_volumes_test.pm ├── ovf_manifests │ ├── Win10-Liz-disk1.vmdk │ ├── Win10-Liz.ovf │ ├── Win10-Liz_no_default_ns.ovf │ ├── Win_2008_R2_two-disks.ovf │ ├── disk1.vmdk │ └── disk2.vmdk ├── parse_volname_test.pm ├── path_to_volume_id_test.pm ├── prune_backups_test.pm ├── rbd_namespace.pl ├── run_bwlimit_tests.pl ├── run_disk_tests.pl ├── run_ovf_tests.pl ├── run_plugin_tests.pl └── run_test_zfspoolplugin.pl └── udev-rbd ├── 50-rbd-pve.rules ├── Makefile └── ceph-rbdnamer-pve /.gitignore: -------------------------------------------------------------------------------- 1 | *.deb 2 | *.1.pod 3 | *.1.gz 4 | -------------------------------------------------------------------------------- /ApiChangeLog: -------------------------------------------------------------------------------- 1 | # API Versioning ChangeLog 2 | 3 | Our API versioning contains an `APIVER` and an `APIAGE`. 4 | The `APIAGE` is the number of versions we're backward compatible with. (iow. things got added 5 | without breaking anything unaware of it.) 6 | 7 | Future changes should be documented in here. 8 | 9 | ## Version 11: 10 | 11 | * Allow declaring storage features via plugin data 12 | 13 | A new `storage_has_feature()` helper function was added that checks a storage plugin's features. 14 | Plugins can indicate support for certain features in their `plugindata`. The first such feature is 15 | `backup-provider`, see below for more details. To declare support for this feature, return 16 | `features => { 'backup-provider' => 1 }` as part of the plugin data. 17 | 18 | * Introduce new_backup_provider() plugin method 19 | 20 | Proxmox VE now supports a `Backup Provider API` that can be used to implement custom backup 21 | solutions tightly integrated in the Proxmox VE stack. See the `PVE::BackupProvider::Plugin::Base` 22 | module for detailed documentation. A backup provider also needs to implement an associated storage 23 | plugin for user-facing integration in Proxmox VE. Such a plugin needds to opt-in to the 24 | `backup-provider` feature (see above) and implement the new_backup_provider() method, returning a 25 | blessed reference to the backup provider class. The rest of the plugin methods, e.g. listing 26 | content, providing usage information, etc., follow the same API as usual. 27 | 28 | * Allow declaring sensitive properties via plugin data 29 | 30 | A new `sensitive_properties()` helper function was added to get the list of sensitive properties 31 | a plugin uses via the plugin's `plugindata`. The sensitive properties are passed separately from 32 | other properties to the `on_add_hook()` and `on_update_hook()` methods and should not be written 33 | to the storage configuration file directly, but stored in the more restricted 34 | `/etc/pve/priv/storage` directory on the Proxmox Cluster File System. For example, to declare that 35 | a `ssh-private-key` property used by the plugin is sensitive, return 36 | `'sensitive-properties' => { 'ssh-private-key' => 1 }` as part of the plugin data. The list of 37 | sensitive properties was hard-coded previously, as `encryption-key`, `keyring`, `master-pubkey`, 38 | `password`. For backwards compatibility, this list is still used if a plugin doesn't declare its 39 | own sensitive properties. 40 | 41 | ## Version 10: 42 | 43 | * a new `rename_volume` method has been added 44 | 45 | Storage plugins with rename support need to enable 46 | the `rename` feature flag; e.g. in the `volume_has_feature` method. 47 | 48 | * Replace `volume_snapshot_list` with `volume_snapshot_info`: 49 | 50 | `volume_snapshot_list` was used exclusively by replication and currently, replication is only 51 | allowed for the storage type `zfspool`. Thus, no external plugins should be affected by this 52 | change and `APIAGE` is *not* reset. 53 | 54 | `volume_snapshot_info` returns a hash with snapshot names as keys and `id` and `timestamp` data 55 | for each snapshot, rather than just an array of snaphsot names like `volume_snapshot_list` did. 56 | 57 | * Add `blockers` parameter to `volume_rollback_is_possible`: 58 | 59 | The parameter *can* be used to return a list of snapshots that is currently preventing rollback. 60 | 61 | * Replace get/update_volume_notes with generic get/update_volume_attribute 62 | 63 | falling back to the old implementation for notes until we reset APIAGE. the 64 | new method optionally also supports querying/setting a protected flag. 65 | 66 | ## Version 9: (AGE resets to 0): 67 | 68 | * volume_import_formats gets a new parameter *inserted*: 69 | 70 | Old signature: 71 | sub($plugin, $scfg, $storeid, $volname, $base_snapshot, $with_snapshots) 72 | New signature: 73 | sub($plugin, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) 74 | 75 | This is now the same as `volume_export_formats`. 76 | 77 | The same goes for calls to `PVE::Storage::volume_import_formats`, which now 78 | takes a `$snapshot` parameter in the same place. 79 | 80 | * $with_snapshots *may* now be an array reference containing an ordered list of 81 | snapshots, but *may* also just be a boolean, and the contained list *may* be 82 | ignored, so it can still be treated as a boolean. 83 | 84 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include /usr/share/dpkg/pkg-info.mk 2 | 3 | PACKAGE=libpve-storage-perl 4 | BUILDDIR ?= $(PACKAGE)-$(DEB_VERSION) 5 | DSC=$(PACKAGE)_$(DEB_VERSION).dsc 6 | 7 | GITVERSION:=$(shell git rev-parse HEAD) 8 | 9 | DEB=$(PACKAGE)_$(DEB_VERSION_UPSTREAM_REVISION)_all.deb 10 | 11 | all: 12 | 13 | .PHONY: dinstall 14 | dinstall: deb 15 | dpkg -i $(DEB) 16 | 17 | $(BUILDDIR): 18 | rm -rf $@ $@.tmp 19 | cp -a src $@.tmp 20 | cp -a debian $@.tmp/ 21 | echo "git clone git://git.proxmox.com/git/pve-storage.git\\ngit checkout $(GITVERSION)" >$@.tmp/debian/SOURCE 22 | mv $@.tmp $@ 23 | 24 | .PHONY: deb 25 | deb: $(DEB) 26 | $(DEB): $(BUILDDIR) 27 | cd $(BUILDDIR); dpkg-buildpackage -b -us -uc 28 | lintian $(DEB) 29 | 30 | .PHONY: clean distclean 31 | distclean: clean 32 | clean: 33 | rm -rf $(PACKAGE)-[0-9]*/ *.deb *.dsc *.build *.buildinfo *.changes $(PACKAGE)*.tar.* 34 | 35 | .PHONY: upload 36 | upload: UPLOAD_DIST ?= $(DEB_DISTRIBUTION) 37 | upload: $(DEB) 38 | tar cf - $(DEB) | ssh -X repoman@repo.proxmox.com -- upload --product pve --dist $(UPLOAD_DIST) 39 | 40 | dsc: $(DSC) 41 | $(MAKE) clean 42 | $(MAKE) $(DSC) 43 | lintian $(DSC) 44 | 45 | $(DSC): $(BUILDDIR) 46 | cd $(BUILDDIR); dpkg-buildpackage -S -us -uc -d 47 | 48 | sbuild: $(DSC) 49 | sbuild $(DSC) 50 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libpve-storage-perl 2 | Section: perl 3 | Priority: optional 4 | Maintainer: Proxmox Support Team 5 | Build-Depends: debhelper-compat (= 13), 6 | libfile-chdir-perl, 7 | libposix-strptime-perl, 8 | libpve-access-control, 9 | libpve-cluster-perl (>= 8.0.6), 10 | libpve-common-perl (>= 8.2.3), 11 | librados2-perl, 12 | libtest-mockmodule-perl, 13 | libxml-libxml-perl, 14 | lintian, 15 | perl, 16 | pve-cluster (>= 5.0-32), 17 | pve-doc-generator (>= 5.3-3), 18 | pve-qemu-kvm | qemu-utils, 19 | zfsutils-linux, 20 | Standards-Version: 4.6.2 21 | Homepage: https://www.proxmox.com 22 | 23 | Package: libpve-storage-perl 24 | Architecture: all 25 | Breaks: libpve-guest-common-perl (<< 4.0-3), 26 | libpve-http-server-perl (<< 4.0-3), 27 | pve-container (<< 3.1-2), 28 | pve-manager (<< 5.2-12), 29 | qemu-server (<< 8.3.2), 30 | Depends: bzip2, 31 | ceph-common (>= 12.2~), 32 | ceph-fuse, 33 | cifs-utils, 34 | cstream, 35 | glusterfs-client (>= 3.4.0-2), 36 | libfile-chdir-perl, 37 | libposix-strptime-perl, 38 | libpve-access-control (>= 8.1.2), 39 | libpve-apiclient-perl (>= 3.1-1), 40 | libpve-cluster-perl (>= 8.0.6), 41 | libpve-common-perl (>= 8.2.9), 42 | librados2-perl, 43 | libxml-libxml-perl, 44 | lvm2, 45 | lzop, 46 | nfs-common, 47 | proxmox-backup-client (>= 2.1.10~), 48 | proxmox-backup-file-restore, 49 | pve-cluster (>= 5.0-32), 50 | smartmontools, 51 | smbclient, 52 | thin-provisioning-tools, 53 | udev, 54 | zstd, 55 | ${misc:Depends}, 56 | ${perl:Depends}, 57 | Recommends: pve-esxi-import-tools (>= 0.6.0), 58 | zfs-zed, 59 | Description: Proxmox VE storage management library 60 | This package contains the storage management library used by Proxmox VE. 61 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010 - 2024 Proxmox Server Solutions GmbH 2 | 3 | This software is written by Proxmox Server Solutions GmbH 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | debian/SOURCE 2 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | #DEBHELPER# 6 | 7 | case "$1" in 8 | configure) 9 | if test -n "$2"; then 10 | 11 | # TODO: remove once PVE 8.0 is released 12 | if dpkg --compare-versions "$2" 'lt' '7.0-3'; then 13 | warning="Warning: failed to move old CIFS credential file, cluster not quorate?" 14 | for file in /etc/pve/priv/*.cred; do 15 | if [ -f "$file" ]; then 16 | echo "Info: found CIFS credentials using old path: $file" >&2 17 | mkdir -p "/etc/pve/priv/storage" || { echo "$warning" && continue; } 18 | base=$(basename --suffix=".cred" "$file") 19 | target="/etc/pve/priv/storage/$base.pw" 20 | if [ -f "$target" ]; then 21 | if diff "$file" "$target" >&2 > /dev/null; then 22 | echo "Info: removing $file, because it is identical to $target" >&2 23 | rm "$file" || { echo "$warning" && continue; } 24 | else 25 | echo "Warning: not renaming $file, because $target already exists and differs!" >&2 26 | fi 27 | else 28 | echo "Info: renaming $file to $target" >&2 29 | mv "$file" "$target" || { echo "$warning" && continue; } 30 | fi 31 | fi 32 | done 33 | fi 34 | fi 35 | ;; 36 | 37 | esac 38 | 39 | exit 0 40 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | %: 13 | dh $@ 14 | -------------------------------------------------------------------------------- /debian/triggers: -------------------------------------------------------------------------------- 1 | activate-noawait pve-api-updates 2 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | DESTDIR= 2 | PREFIX=/usr 3 | 4 | export PERLDIR=$(PREFIX)/share/perl5 5 | 6 | all: 7 | 8 | .PHONY: install 9 | install: PVE bin udev-rbd 10 | $(MAKE) -C bin install 11 | $(MAKE) -C PVE install 12 | $(MAKE) -C udev-rbd install 13 | 14 | .PHONY: test 15 | test: 16 | perl -I. -T -e "use PVE::CLI::pvesm; PVE::CLI::pvesm->verify_api();" 17 | $(MAKE) -C test 18 | $(MAKE) -C PVE test 19 | 20 | .PHONY: clean 21 | clean: 22 | $(MAKE) -C bin clean 23 | $(MAKE) -C PVE clean 24 | $(MAKE) -C udev-rbd clean 25 | -------------------------------------------------------------------------------- /src/PVE/API2/Disks/LVM.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::Disks::LVM; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::Storage::LVMPlugin; 7 | use PVE::Diskmanage; 8 | use PVE::JSONSchema qw(get_standard_option); 9 | use PVE::API2::Storage::Config; 10 | use PVE::Tools qw(lock_file run_command); 11 | 12 | use PVE::RPCEnvironment; 13 | use PVE::RESTHandler; 14 | 15 | use base qw(PVE::RESTHandler); 16 | 17 | __PACKAGE__->register_method ({ 18 | name => 'index', 19 | path => '', 20 | method => 'GET', 21 | proxyto => 'node', 22 | protected => 1, 23 | permissions => { 24 | check => ['perm', '/', ['Sys.Audit']], 25 | }, 26 | description => "List LVM Volume Groups", 27 | parameters => { 28 | additionalProperties => 0, 29 | properties => { 30 | node => get_standard_option('pve-node'), 31 | }, 32 | }, 33 | returns => { 34 | type => 'object', 35 | properties => { 36 | leaf => { 37 | type => 'boolean', 38 | }, 39 | children => { 40 | type => 'array', 41 | items => { 42 | type => "object", 43 | properties => { 44 | leaf => { 45 | type => 'boolean', 46 | }, 47 | name => { 48 | type => 'string', 49 | description => 'The name of the volume group', 50 | }, 51 | size => { 52 | type => 'integer', 53 | description => 'The size of the volume group in bytes', 54 | }, 55 | free => { 56 | type => 'integer', 57 | description => 'The free bytes in the volume group', 58 | }, 59 | children => { 60 | optional => 1, 61 | type => 'array', 62 | description => 'The underlying physical volumes', 63 | items => { 64 | type => 'object', 65 | properties => { 66 | leaf => { 67 | type => 'boolean', 68 | }, 69 | name => { 70 | type => 'string', 71 | description => 'The name of the physical volume', 72 | }, 73 | size => { 74 | type => 'integer', 75 | description => 'The size of the physical volume in bytes', 76 | }, 77 | free => { 78 | type => 'integer', 79 | description => 'The free bytes in the physical volume', 80 | }, 81 | }, 82 | }, 83 | }, 84 | }, 85 | }, 86 | }, 87 | }, 88 | }, 89 | code => sub { 90 | my ($param) = @_; 91 | 92 | my $result = []; 93 | 94 | my $vgs = PVE::Storage::LVMPlugin::lvm_vgs(1); 95 | 96 | foreach my $vg_name (sort keys %$vgs) { 97 | my $vg = $vgs->{$vg_name}; 98 | $vg->{name} = $vg_name; 99 | $vg->{leaf} = 0; 100 | foreach my $pv (@{$vg->{pvs}}) { 101 | $pv->{leaf} = 1; 102 | } 103 | $vg->{children} = delete $vg->{pvs}; 104 | push @$result, $vg; 105 | } 106 | 107 | return { 108 | leaf => 0, 109 | children => $result, 110 | }; 111 | }}); 112 | 113 | __PACKAGE__->register_method ({ 114 | name => 'create', 115 | path => '', 116 | method => 'POST', 117 | proxyto => 'node', 118 | protected => 1, 119 | permissions => { 120 | description => "Requires additionally 'Datastore.Allocate' on /storage when setting 'add_storage'", 121 | check => ['perm', '/', ['Sys.Modify']], 122 | }, 123 | description => "Create an LVM Volume Group", 124 | parameters => { 125 | additionalProperties => 0, 126 | properties => { 127 | node => get_standard_option('pve-node'), 128 | name => get_standard_option('pve-storage-id'), 129 | device => { 130 | type => 'string', 131 | description => 'The block device you want to create the volume group on', 132 | }, 133 | add_storage => { 134 | description => "Configure storage using the Volume Group", 135 | type => 'boolean', 136 | optional => 1, 137 | default => 0, 138 | }, 139 | }, 140 | }, 141 | returns => { type => 'string' }, 142 | code => sub { 143 | my ($param) = @_; 144 | 145 | my $rpcenv = PVE::RPCEnvironment::get(); 146 | my $user = $rpcenv->get_user(); 147 | 148 | my $name = $param->{name}; 149 | my $dev = $param->{device}; 150 | my $node = $param->{node}; 151 | 152 | $dev = PVE::Diskmanage::verify_blockdev_path($dev); 153 | PVE::Diskmanage::assert_disk_unused($dev); 154 | 155 | my $storage_params = { 156 | type => 'lvm', 157 | vgname => $name, 158 | storage => $name, 159 | content => 'rootdir,images', 160 | shared => 0, 161 | nodes => $node, 162 | }; 163 | my $verify_params = [qw(vgname)]; 164 | 165 | if ($param->{add_storage}) { 166 | $rpcenv->check($user, "/storage", ['Datastore.Allocate']); 167 | 168 | # reserve the name and add as disabled, will be enabled below if creation works out 169 | PVE::API2::Storage::Config->create_or_update( 170 | $name, $node, $storage_params, $verify_params, 1); 171 | } 172 | 173 | my $worker = sub { 174 | PVE::Diskmanage::locked_disk_action(sub { 175 | PVE::Diskmanage::assert_disk_unused($dev); 176 | die "volume group with name '${name}' already exists on node '${node}'\n" 177 | if PVE::Storage::LVMPlugin::lvm_vgs()->{$name}; 178 | 179 | if (PVE::Diskmanage::is_partition($dev)) { 180 | eval { PVE::Diskmanage::change_parttype($dev, '8E00'); }; 181 | warn $@ if $@; 182 | } 183 | 184 | PVE::Storage::LVMPlugin::lvm_create_volume_group($dev, $name); 185 | 186 | PVE::Diskmanage::udevadm_trigger($dev); 187 | 188 | if ($param->{add_storage}) { 189 | PVE::API2::Storage::Config->create_or_update( 190 | $name, $node, $storage_params, $verify_params); 191 | } 192 | }); 193 | }; 194 | 195 | return $rpcenv->fork_worker('lvmcreate', $name, $user, $worker); 196 | }}); 197 | 198 | __PACKAGE__->register_method ({ 199 | name => 'delete', 200 | path => '{name}', 201 | method => 'DELETE', 202 | proxyto => 'node', 203 | protected => 1, 204 | permissions => { 205 | description => "Requires additionally 'Datastore.Allocate' on /storage when setting 'cleanup-config'", 206 | check => ['perm', '/', ['Sys.Modify']], 207 | }, 208 | description => "Remove an LVM Volume Group.", 209 | parameters => { 210 | additionalProperties => 0, 211 | properties => { 212 | node => get_standard_option('pve-node'), 213 | name => get_standard_option('pve-storage-id'), 214 | 'cleanup-config' => { 215 | description => "Marks associated storage(s) as not available on this node anymore ". 216 | "or removes them from the configuration (if configured for this node only).", 217 | type => 'boolean', 218 | optional => 1, 219 | default => 0, 220 | }, 221 | 'cleanup-disks' => { 222 | description => "Also wipe disks so they can be repurposed afterwards.", 223 | type => 'boolean', 224 | optional => 1, 225 | default => 0, 226 | }, 227 | }, 228 | }, 229 | returns => { type => 'string' }, 230 | code => sub { 231 | my ($param) = @_; 232 | 233 | my $rpcenv = PVE::RPCEnvironment::get(); 234 | my $user = $rpcenv->get_user(); 235 | 236 | $rpcenv->check($user, "/storage", ['Datastore.Allocate']) if $param->{'cleanup-config'}; 237 | 238 | my $name = $param->{name}; 239 | my $node = $param->{node}; 240 | 241 | my $worker = sub { 242 | PVE::Diskmanage::locked_disk_action(sub { 243 | my $vgs = PVE::Storage::LVMPlugin::lvm_vgs(1); 244 | die "no such volume group '$name'\n" if !$vgs->{$name}; 245 | 246 | PVE::Storage::LVMPlugin::lvm_destroy_volume_group($name); 247 | 248 | my $config_err; 249 | if ($param->{'cleanup-config'}) { 250 | my $match = sub { 251 | my ($scfg) = @_; 252 | return $scfg->{type} eq 'lvm' && $scfg->{vgname} eq $name; 253 | }; 254 | eval { PVE::API2::Storage::Config->cleanup_storages_for_node($match, $node); }; 255 | warn $config_err = $@ if $@; 256 | } 257 | 258 | if ($param->{'cleanup-disks'}) { 259 | my $wiped = []; 260 | eval { 261 | for my $pv ($vgs->{$name}->{pvs}->@*) { 262 | my $dev = PVE::Diskmanage::verify_blockdev_path($pv->{name}); 263 | PVE::Diskmanage::wipe_blockdev($dev); 264 | push $wiped->@*, $dev; 265 | } 266 | }; 267 | my $err = $@; 268 | PVE::Diskmanage::udevadm_trigger($wiped->@*); 269 | die "cleanup failed - $err" if $err; 270 | } 271 | 272 | die "config cleanup failed - $config_err" if $config_err; 273 | }); 274 | }; 275 | 276 | return $rpcenv->fork_worker('lvmremove', $name, $user, $worker); 277 | }}); 278 | 279 | 1; 280 | -------------------------------------------------------------------------------- /src/PVE/API2/Disks/LVMThin.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::Disks::LVMThin; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::Storage::LvmThinPlugin; 7 | use PVE::Diskmanage; 8 | use PVE::JSONSchema qw(get_standard_option); 9 | use PVE::API2::Storage::Config; 10 | use PVE::Storage; 11 | use PVE::Tools qw(run_command lock_file); 12 | 13 | use PVE::RPCEnvironment; 14 | use PVE::RESTHandler; 15 | 16 | use base qw(PVE::RESTHandler); 17 | 18 | __PACKAGE__->register_method ({ 19 | name => 'index', 20 | path => '', 21 | method => 'GET', 22 | proxyto => 'node', 23 | protected => 1, 24 | permissions => { 25 | check => ['perm', '/', ['Sys.Audit']], 26 | }, 27 | description => "List LVM thinpools", 28 | parameters => { 29 | additionalProperties => 0, 30 | properties => { 31 | node => get_standard_option('pve-node'), 32 | }, 33 | }, 34 | returns => { 35 | type => 'array', 36 | items => { 37 | type => 'object', 38 | properties => { 39 | lv => { 40 | type => 'string', 41 | description => 'The name of the thinpool.', 42 | }, 43 | vg => { 44 | type => 'string', 45 | description => 'The associated volume group.', 46 | }, 47 | lv_size => { 48 | type => 'integer', 49 | description => 'The size of the thinpool in bytes.', 50 | }, 51 | used => { 52 | type => 'integer', 53 | description => 'The used bytes of the thinpool.', 54 | }, 55 | metadata_size => { 56 | type => 'integer', 57 | description => 'The size of the metadata lv in bytes.', 58 | }, 59 | metadata_used => { 60 | type => 'integer', 61 | description => 'The used bytes of the metadata lv.', 62 | }, 63 | }, 64 | }, 65 | }, 66 | code => sub { 67 | my ($param) = @_; 68 | return PVE::Storage::LvmThinPlugin::list_thinpools(undef); 69 | }}); 70 | 71 | __PACKAGE__->register_method ({ 72 | name => 'create', 73 | path => '', 74 | method => 'POST', 75 | proxyto => 'node', 76 | protected => 1, 77 | permissions => { 78 | description => "Requires additionally 'Datastore.Allocate' on /storage when setting 'add_storage'", 79 | check => ['perm', '/', ['Sys.Modify']], 80 | }, 81 | description => "Create an LVM thinpool", 82 | parameters => { 83 | additionalProperties => 0, 84 | properties => { 85 | node => get_standard_option('pve-node'), 86 | name => get_standard_option('pve-storage-id'), 87 | device => { 88 | type => 'string', 89 | description => 'The block device you want to create the thinpool on.', 90 | }, 91 | add_storage => { 92 | description => "Configure storage using the thinpool.", 93 | type => 'boolean', 94 | optional => 1, 95 | default => 0, 96 | }, 97 | }, 98 | }, 99 | returns => { type => 'string' }, 100 | code => sub { 101 | my ($param) = @_; 102 | 103 | my $rpcenv = PVE::RPCEnvironment::get(); 104 | my $user = $rpcenv->get_user(); 105 | 106 | my $name = $param->{name}; 107 | my $dev = $param->{device}; 108 | my $node = $param->{node}; 109 | 110 | $dev = PVE::Diskmanage::verify_blockdev_path($dev); 111 | PVE::Diskmanage::assert_disk_unused($dev); 112 | 113 | my $storage_params = { 114 | type => 'lvmthin', 115 | vgname => $name, 116 | thinpool => $name, 117 | storage => $name, 118 | content => 'rootdir,images', 119 | nodes => $node, 120 | }; 121 | my $verify_params = [qw(vgname thinpool)]; 122 | 123 | if ($param->{add_storage}) { 124 | $rpcenv->check($user, "/storage", ['Datastore.Allocate']); 125 | 126 | # reserve the name and add as disabled, will be enabled below if creation works out 127 | PVE::API2::Storage::Config->create_or_update( 128 | $name, $node, $storage_params, $verify_params, 1); 129 | } 130 | 131 | my $worker = sub { 132 | PVE::Diskmanage::locked_disk_action(sub { 133 | PVE::Diskmanage::assert_disk_unused($dev); 134 | 135 | die "volume group with name '${name}' already exists on node '${node}'\n" 136 | if PVE::Storage::LVMPlugin::lvm_vgs()->{$name}; 137 | 138 | if (PVE::Diskmanage::is_partition($dev)) { 139 | eval { PVE::Diskmanage::change_parttype($dev, '8E00'); }; 140 | warn $@ if $@; 141 | } 142 | 143 | PVE::Storage::LVMPlugin::lvm_create_volume_group($dev, $name); 144 | my $pv = PVE::Storage::LVMPlugin::lvm_pv_info($dev); 145 | # keep some free space just in case 146 | my $datasize = $pv->{size} - 128*1024; 147 | # default to 1% for metadata 148 | my $metadatasize = $datasize/100; 149 | # but at least 1G, as recommended in lvmthin man 150 | $metadatasize = 1024*1024 if $metadatasize < 1024*1024; 151 | # but at most 16G, which is the current lvm max 152 | $metadatasize = 16*1024*1024 if $metadatasize > 16*1024*1024; 153 | # shrink data by needed amount for metadata 154 | $datasize -= 2*$metadatasize; 155 | 156 | run_command([ 157 | '/sbin/lvcreate', 158 | '--type', 'thin-pool', 159 | "-L${datasize}K", 160 | '--poolmetadatasize', "${metadatasize}K", 161 | '-n', $name, 162 | $name 163 | ]); 164 | 165 | PVE::Diskmanage::udevadm_trigger($dev); 166 | 167 | if ($param->{add_storage}) { 168 | PVE::API2::Storage::Config->create_or_update( 169 | $name, $node, $storage_params, $verify_params); 170 | } 171 | }); 172 | }; 173 | 174 | return $rpcenv->fork_worker('lvmthincreate', $name, $user, $worker); 175 | }}); 176 | 177 | __PACKAGE__->register_method ({ 178 | name => 'delete', 179 | path => '{name}', 180 | method => 'DELETE', 181 | proxyto => 'node', 182 | protected => 1, 183 | permissions => { 184 | description => "Requires additionally 'Datastore.Allocate' on /storage when setting 'cleanup-config'", 185 | check => ['perm', '/', ['Sys.Modify']], 186 | }, 187 | description => "Remove an LVM thin pool.", 188 | parameters => { 189 | additionalProperties => 0, 190 | properties => { 191 | node => get_standard_option('pve-node'), 192 | name => get_standard_option('pve-storage-id'), 193 | 'volume-group' => get_standard_option('pve-storage-id'), 194 | 'cleanup-config' => { 195 | description => "Marks associated storage(s) as not available on this node anymore ". 196 | "or removes them from the configuration (if configured for this node only).", 197 | type => 'boolean', 198 | optional => 1, 199 | default => 0, 200 | }, 201 | 'cleanup-disks' => { 202 | description => "Also wipe disks so they can be repurposed afterwards.", 203 | type => 'boolean', 204 | optional => 1, 205 | default => 0, 206 | }, 207 | }, 208 | }, 209 | returns => { type => 'string' }, 210 | code => sub { 211 | my ($param) = @_; 212 | 213 | my $rpcenv = PVE::RPCEnvironment::get(); 214 | my $user = $rpcenv->get_user(); 215 | 216 | $rpcenv->check($user, "/storage", ['Datastore.Allocate']) if $param->{'cleanup-config'}; 217 | 218 | my $vg = $param->{'volume-group'}; 219 | my $lv = $param->{name}; 220 | my $node = $param->{node}; 221 | 222 | my $worker = sub { 223 | PVE::Diskmanage::locked_disk_action(sub { 224 | my $thinpools = PVE::Storage::LvmThinPlugin::list_thinpools(); 225 | 226 | die "no such thin pool ${vg}/${lv}\n" 227 | if !grep { $_->{lv} eq $lv && $_->{vg} eq $vg } $thinpools->@*; 228 | 229 | run_command(['lvremove', '-y', "${vg}/${lv}"]); 230 | 231 | my $config_err; 232 | if ($param->{'cleanup-config'}) { 233 | my $match = sub { 234 | my ($scfg) = @_; 235 | return $scfg->{type} eq 'lvmthin' 236 | && $scfg->{vgname} eq $vg 237 | && $scfg->{thinpool} eq $lv; 238 | }; 239 | eval { PVE::API2::Storage::Config->cleanup_storages_for_node($match, $node); }; 240 | warn $config_err = $@ if $@; 241 | } 242 | 243 | if ($param->{'cleanup-disks'}) { 244 | my $vgs = PVE::Storage::LVMPlugin::lvm_vgs(1); 245 | 246 | die "no such volume group '$vg'\n" if !$vgs->{$vg}; 247 | die "volume group '$vg' still in use\n" if $vgs->{$vg}->{lvcount} > 0; 248 | 249 | my $wiped = []; 250 | eval { 251 | for my $pv ($vgs->{$vg}->{pvs}->@*) { 252 | my $dev = PVE::Diskmanage::verify_blockdev_path($pv->{name}); 253 | PVE::Diskmanage::wipe_blockdev($dev); 254 | push $wiped->@*, $dev; 255 | } 256 | }; 257 | my $err = $@; 258 | PVE::Diskmanage::udevadm_trigger($wiped->@*); 259 | die "cleanup failed - $err" if $err; 260 | } 261 | 262 | die "config cleanup failed - $config_err" if $config_err; 263 | }); 264 | }; 265 | 266 | return $rpcenv->fork_worker('lvmthinremove', "${vg}-${lv}", $user, $worker); 267 | }}); 268 | 269 | 1; 270 | -------------------------------------------------------------------------------- /src/PVE/API2/Disks/Makefile: -------------------------------------------------------------------------------- 1 | 2 | SOURCES= LVM.pm\ 3 | LVMThin.pm\ 4 | ZFS.pm\ 5 | Directory.pm 6 | 7 | .PHONY: install 8 | install: 9 | for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/API2/Disks/$$i; done 10 | -------------------------------------------------------------------------------- /src/PVE/API2/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | .PHONY: install 4 | install: 5 | install -D -m 0644 Disks.pm ${DESTDIR}${PERLDIR}/PVE/API2/Disks.pm 6 | make -C Storage install 7 | make -C Disks install 8 | -------------------------------------------------------------------------------- /src/PVE/API2/Storage/FileRestore.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::Storage::FileRestore; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use MIME::Base64; 7 | use PVE::Exception qw(raise_param_exc); 8 | use PVE::JSONSchema qw(get_standard_option); 9 | use PVE::PBSClient; 10 | use PVE::Storage; 11 | use PVE::Tools qw(extract_param); 12 | 13 | use PVE::RESTHandler; 14 | use base qw(PVE::RESTHandler); 15 | 16 | my $parse_volname_or_id = sub { 17 | my ($storeid, $volume) = @_; 18 | 19 | my $volid; 20 | my ($sid, $volname) = PVE::Storage::parse_volume_id($volume, 1); 21 | 22 | if (defined($sid)) { 23 | raise_param_exc({ volume => "storage ID mismatch ($sid != $storeid)." }) 24 | if $sid ne $storeid; 25 | 26 | $volid = $volume; 27 | } elsif ($volume =~ m/^backup\//) { 28 | $volid = "$storeid:$volume"; 29 | } else { 30 | $volid = "$storeid:backup/$volume"; 31 | } 32 | 33 | return $volid; 34 | }; 35 | 36 | __PACKAGE__->register_method ({ 37 | name => 'list', 38 | path => 'list', 39 | method => 'GET', 40 | proxyto => 'node', 41 | permissions => { 42 | description => "You need read access for the volume.", 43 | user => 'all', 44 | }, 45 | description => "List files and directories for single file restore under the given path.", 46 | parameters => { 47 | additionalProperties => 0, 48 | properties => { 49 | node => get_standard_option('pve-node'), 50 | storage => get_standard_option('pve-storage-id', { 51 | completion => \&PVE::Storage::complete_storage_enabled, 52 | }), 53 | volume => { 54 | description => "Backup volume ID or name. Currently only PBS snapshots are supported.", 55 | type => 'string', 56 | completion => \&PVE::Storage::complete_volume, 57 | }, 58 | filepath => { 59 | description => 'base64-path to the directory or file being listed, or "/".', 60 | type => 'string', 61 | }, 62 | }, 63 | }, 64 | returns => { 65 | type => 'array', 66 | items => { 67 | type => "object", 68 | properties => { 69 | filepath => { 70 | description => "base64 path of the current entry", 71 | type => 'string', 72 | }, 73 | type => { 74 | description => "Entry type.", 75 | type => 'string', 76 | }, 77 | text => { 78 | description => "Entry display text.", 79 | type => 'string', 80 | }, 81 | leaf => { 82 | description => "If this entry is a leaf in the directory graph.", 83 | type => 'boolean', 84 | }, 85 | size => { 86 | description => "Entry file size.", 87 | type => 'integer', 88 | optional => 1, 89 | }, 90 | mtime => { 91 | description => "Entry last-modified time (unix timestamp).", 92 | type => 'integer', 93 | optional => 1, 94 | }, 95 | }, 96 | }, 97 | }, 98 | protected => 1, 99 | code => sub { 100 | my ($param) = @_; 101 | 102 | my $rpcenv = PVE::RPCEnvironment::get(); 103 | my $user = $rpcenv->get_user(); 104 | 105 | my $path = extract_param($param, 'filepath') || "/"; 106 | my $base64 = $path ne "/"; 107 | 108 | my $storeid = extract_param($param, 'storage'); 109 | 110 | my $volid = $parse_volname_or_id->($storeid, $param->{volume}); 111 | my $cfg = PVE::Storage::config(); 112 | my $scfg = PVE::Storage::storage_config($cfg, $storeid); 113 | 114 | PVE::Storage::check_volume_access($rpcenv, $user, $cfg, undef, $volid, 'backup'); 115 | 116 | raise_param_exc({'storage' => "Only PBS storages supported for file-restore."}) 117 | if $scfg->{type} ne 'pbs'; 118 | 119 | my (undef, $snap) = PVE::Storage::parse_volname($cfg, $volid); 120 | 121 | my $client = PVE::PBSClient->new($scfg, $storeid); 122 | my $ret = $client->file_restore_list($snap, $path, $base64, { timeout => 25 }); 123 | 124 | if (ref($ret) eq "HASH") { 125 | my $msg = $ret->{message}; 126 | if (my $code = $ret->{code}) { 127 | die PVE::Exception->new("$msg\n", code => $code); 128 | } else { 129 | die "$msg\n"; 130 | } 131 | } elsif (ref($ret) eq "ARRAY") { 132 | # 'leaf' is a proper JSON boolean, map to perl-y bool 133 | # TODO: make PBSClient decode all bools always as 1/0? 134 | foreach my $item (@$ret) { 135 | $item->{leaf} = $item->{leaf} ? 1 : 0; 136 | } 137 | 138 | return $ret; 139 | } 140 | 141 | die "invalid proxmox-file-restore output"; 142 | }}); 143 | 144 | __PACKAGE__->register_method ({ 145 | name => 'download', 146 | path => 'download', 147 | method => 'GET', 148 | proxyto => 'node', 149 | download_allowed => 1, 150 | permissions => { 151 | description => "You need read access for the volume.", 152 | user => 'all', 153 | }, 154 | description => "Extract a file or directory (as zip archive) from a PBS backup.", 155 | parameters => { 156 | additionalProperties => 0, 157 | properties => { 158 | node => get_standard_option('pve-node'), 159 | storage => get_standard_option('pve-storage-id', { 160 | completion => \&PVE::Storage::complete_storage_enabled, 161 | }), 162 | volume => { 163 | description => "Backup volume ID or name. Currently only PBS snapshots are supported.", 164 | type => 'string', 165 | completion => \&PVE::Storage::complete_volume, 166 | }, 167 | filepath => { 168 | description => 'base64-path to the directory or file to download.', 169 | type => 'string', 170 | }, 171 | tar => { 172 | description => "Download dirs as 'tar.zst' instead of 'zip'.", 173 | type => 'boolean', 174 | optional => 1, 175 | default => 0, 176 | }, 177 | }, 178 | }, 179 | returns => { 180 | type => 'any', # download 181 | }, 182 | protected => 1, 183 | code => sub { 184 | my ($param) = @_; 185 | 186 | my $rpcenv = PVE::RPCEnvironment::get(); 187 | my $user = $rpcenv->get_user(); 188 | 189 | my $path = extract_param($param, 'filepath'); 190 | my $storeid = extract_param($param, 'storage'); 191 | my $volid = $parse_volname_or_id->($storeid, $param->{volume}); 192 | my $tar = extract_param($param, 'tar') // 0; 193 | 194 | my $cfg = PVE::Storage::config(); 195 | my $scfg = PVE::Storage::storage_config($cfg, $storeid); 196 | 197 | PVE::Storage::check_volume_access($rpcenv, $user, $cfg, undef, $volid, 'backup'); 198 | 199 | raise_param_exc({'storage' => "Only PBS storages supported for file-restore."}) 200 | if $scfg->{type} ne 'pbs'; 201 | 202 | my (undef, $snap) = PVE::Storage::parse_volname($cfg, $volid); 203 | 204 | my $client = PVE::PBSClient->new($scfg, $storeid); 205 | my $fifo = $client->file_restore_extract_prepare(); 206 | 207 | $rpcenv->fork_worker('pbs-download', undef, $user, sub { 208 | my $name = decode_base64($path); 209 | print "Starting download of file: $name\n"; 210 | $client->file_restore_extract($fifo, $snap, $path, 1, $tar); 211 | }); 212 | 213 | my $ret = { 214 | download => { 215 | path => $fifo, 216 | stream => 1, 217 | 'content-type' => 'application/octet-stream', 218 | }, 219 | }; 220 | return $ret; 221 | }}); 222 | 223 | 1; 224 | -------------------------------------------------------------------------------- /src/PVE/API2/Storage/Makefile: -------------------------------------------------------------------------------- 1 | 2 | SOURCES= Content.pm Status.pm Config.pm PruneBackups.pm Scan.pm FileRestore.pm 3 | 4 | .PHONY: install 5 | install: 6 | for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/API2/Storage/$$i; done 7 | -------------------------------------------------------------------------------- /src/PVE/API2/Storage/PruneBackups.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::Storage::PruneBackups; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::Cluster; 7 | use PVE::JSONSchema qw(get_standard_option); 8 | use PVE::RESTHandler; 9 | use PVE::RPCEnvironment; 10 | use PVE::Storage; 11 | use PVE::Tools qw(extract_param); 12 | 13 | use base qw(PVE::RESTHandler); 14 | 15 | __PACKAGE__->register_method ({ 16 | name => 'dryrun', 17 | path => '', 18 | method => 'GET', 19 | description => "Get prune information for backups. NOTE: this is only a preview and might not be " . 20 | "what a subsequent prune call does if backups are removed/added in the meantime.", 21 | permissions => { 22 | check => ['perm', '/storage/{storage}', ['Datastore.Audit', 'Datastore.AllocateSpace'], any => 1], 23 | }, 24 | protected => 1, 25 | proxyto => 'node', 26 | parameters => { 27 | additionalProperties => 0, 28 | properties => { 29 | node => get_standard_option('pve-node'), 30 | storage => get_standard_option('pve-storage-id', { 31 | completion => \&PVE::Storage::complete_storage_enabled, 32 | }), 33 | 'prune-backups' => get_standard_option('prune-backups', { 34 | description => "Use these retention options instead of those from the storage configuration.", 35 | optional => 1, 36 | }), 37 | type => { 38 | description => "Either 'qemu' or 'lxc'. Only consider backups for guests of this type.", 39 | type => 'string', 40 | optional => 1, 41 | enum => ['qemu', 'lxc'], 42 | }, 43 | vmid => get_standard_option('pve-vmid', { 44 | description => "Only consider backups for this guest.", 45 | optional => 1, 46 | completion => \&PVE::Cluster::complete_vmid, 47 | }), 48 | }, 49 | }, 50 | returns => { 51 | type => 'array', 52 | items => { 53 | type => 'object', 54 | properties => { 55 | volid => { 56 | description => "Backup volume ID.", 57 | type => 'string', 58 | }, 59 | 'ctime' => { 60 | description => "Creation time of the backup (seconds since the UNIX epoch).", 61 | type => 'integer', 62 | }, 63 | 'mark' => { 64 | description => "Whether the backup would be kept or removed. Backups that are" . 65 | " protected or don't use the standard naming scheme are not removed.", 66 | type => 'string', 67 | enum => ['keep', 'remove', 'protected', 'renamed'], 68 | }, 69 | type => { 70 | description => "One of 'qemu', 'lxc', 'openvz' or 'unknown'.", 71 | type => 'string', 72 | }, 73 | 'vmid' => { 74 | description => "The VM the backup belongs to.", 75 | type => 'integer', 76 | optional => 1, 77 | }, 78 | }, 79 | }, 80 | }, 81 | code => sub { 82 | my ($param) = @_; 83 | 84 | my $cfg = PVE::Storage::config(); 85 | 86 | my $vmid = extract_param($param, 'vmid'); 87 | my $type = extract_param($param, 'type'); 88 | my $storeid = extract_param($param, 'storage'); 89 | 90 | my $prune_backups = extract_param($param, 'prune-backups'); 91 | $prune_backups = PVE::JSONSchema::parse_property_string('prune-backups', $prune_backups) 92 | if defined($prune_backups); 93 | 94 | return PVE::Storage::prune_backups($cfg, $storeid, $prune_backups, $vmid, $type, 1); 95 | }}); 96 | 97 | __PACKAGE__->register_method ({ 98 | name => 'delete', 99 | path => '', 100 | method => 'DELETE', 101 | description => "Prune backups. Only those using the standard naming scheme are considered.", 102 | permissions => { 103 | description => "You need the 'Datastore.Allocate' privilege on the storage " . 104 | "(or if a VM ID is specified, 'Datastore.AllocateSpace' and 'VM.Backup' for the VM).", 105 | user => 'all', 106 | }, 107 | protected => 1, 108 | proxyto => 'node', 109 | parameters => { 110 | additionalProperties => 0, 111 | properties => { 112 | node => get_standard_option('pve-node'), 113 | storage => get_standard_option('pve-storage-id', { 114 | completion => \&PVE::Storage::complete_storage, 115 | }), 116 | 'prune-backups' => get_standard_option('prune-backups', { 117 | description => "Use these retention options instead of those from the storage configuration.", 118 | }), 119 | type => { 120 | description => "Either 'qemu' or 'lxc'. Only consider backups for guests of this type.", 121 | type => 'string', 122 | optional => 1, 123 | enum => ['qemu', 'lxc'], 124 | }, 125 | vmid => get_standard_option('pve-vmid', { 126 | description => "Only prune backups for this VM.", 127 | completion => \&PVE::Cluster::complete_vmid, 128 | optional => 1, 129 | }), 130 | }, 131 | }, 132 | returns => { type => 'string' }, 133 | code => sub { 134 | my ($param) = @_; 135 | 136 | my $rpcenv = PVE::RPCEnvironment::get(); 137 | my $authuser = $rpcenv->get_user(); 138 | 139 | my $cfg = PVE::Storage::config(); 140 | 141 | my $vmid = extract_param($param, 'vmid'); 142 | my $type = extract_param($param, 'type'); 143 | my $storeid = extract_param($param, 'storage'); 144 | 145 | my $prune_backups = extract_param($param, 'prune-backups'); 146 | $prune_backups = PVE::JSONSchema::parse_property_string('prune-backups', $prune_backups) 147 | if defined($prune_backups); 148 | 149 | if (defined($vmid)) { 150 | $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']); 151 | $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup']); 152 | } else { 153 | $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.Allocate']); 154 | } 155 | 156 | my $id = (defined($vmid) ? "$vmid@" : '') . $storeid; 157 | my $worker = sub { 158 | PVE::Storage::prune_backups($cfg, $storeid, $prune_backups, $vmid, $type, 0); 159 | }; 160 | 161 | return $rpcenv->fork_worker('prunebackups', $id, $authuser, $worker); 162 | }}); 163 | 164 | 1; 165 | -------------------------------------------------------------------------------- /src/PVE/BackupProvider/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | install: 3 | make -C Plugin install 4 | -------------------------------------------------------------------------------- /src/PVE/BackupProvider/Plugin/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES = Base.pm 2 | 3 | .PHONY: install 4 | install: 5 | for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/BackupProvider/Plugin/$$i; done 6 | -------------------------------------------------------------------------------- /src/PVE/CLI/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES=pvesm.pm 2 | 3 | .PHONY: install 4 | install: ${SOURCES} 5 | install -d -m 0755 ${DESTDIR}${PERLDIR}/PVE/CLI 6 | for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/CLI/$$i; done 7 | 8 | 9 | clean: 10 | -------------------------------------------------------------------------------- /src/PVE/GuestImport.pm: -------------------------------------------------------------------------------- 1 | package PVE::GuestImport; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Path; 7 | 8 | use PVE::Storage; 9 | use PVE::Tools qw(run_command); 10 | 11 | sub extract_disk_from_import_file { 12 | my ($volid, $vmid, $target_storeid) = @_; 13 | 14 | my ($source_storeid, $volname) = PVE::Storage::parse_volume_id($volid); 15 | $target_storeid //= $source_storeid; 16 | my $cfg = PVE::Storage::config(); 17 | 18 | my ($vtype, $name, undef, undef, undef, undef, $fmt) = 19 | PVE::Storage::parse_volname($cfg, $volid); 20 | 21 | die "only files with content type 'import' can be extracted\n" 22 | if $vtype ne 'import'; 23 | 24 | die "only files from 'ova' format can be extracted\n" 25 | if $fmt !~ m/^ova\+/; 26 | 27 | # extract the inner file from the name 28 | my $archive_volid; 29 | my $inner_file; 30 | my $inner_fmt; 31 | if ($name =~ m!^(.*\.ova)/(${PVE::Storage::SAFE_CHAR_WITH_WHITESPACE_CLASS_RE}+)$!) { 32 | $archive_volid = "$source_storeid:import/$1"; 33 | $inner_file = $2; 34 | ($inner_fmt) = $fmt =~ /^ova\+(.*)$/; 35 | } else { 36 | die "cannot extract $volid - invalid volname $volname\n"; 37 | } 38 | 39 | die "cannot determine format of '$volid'\n" if !$inner_fmt; 40 | 41 | my $ova_path = PVE::Storage::path($cfg, $archive_volid); 42 | 43 | my $tmpdir = PVE::Storage::get_image_dir($cfg, $target_storeid, $vmid); 44 | my $pid = $$; 45 | $tmpdir .= "/tmp_${pid}_${vmid}"; 46 | mkpath $tmpdir; 47 | 48 | my $source_path = "$tmpdir/$inner_file"; 49 | my $target_path; 50 | my $target_volid; 51 | eval { 52 | run_command([ 53 | 'tar', 54 | '-x', 55 | '--force-local', 56 | '--no-same-owner', 57 | '-C', $tmpdir, 58 | '-f', $ova_path, 59 | $inner_file, 60 | ]); 61 | 62 | # check for symlinks and other non regular files 63 | if (-l $source_path || ! -f $source_path) { 64 | die "extracted file '$inner_file' from archive '$archive_volid' is not a regular file\n"; 65 | } 66 | 67 | # check potentially untrusted image file! 68 | PVE::Storage::file_size_info($source_path, undef, $inner_fmt, 1); 69 | 70 | # create temporary 1M image that will get overwritten by the rename 71 | # to reserve the filename and take care of locking 72 | $target_volid = PVE::Storage::vdisk_alloc($cfg, $target_storeid, $vmid, $inner_fmt, undef, 1024); 73 | $target_path = PVE::Storage::path($cfg, $target_volid); 74 | 75 | print "renaming $source_path to $target_path\n"; 76 | 77 | rename($source_path, $target_path) or die "unable to move - $!\n"; 78 | }; 79 | if (my $err = $@) { 80 | File::Path::remove_tree($tmpdir); 81 | die "error during extraction: $err\n"; 82 | } 83 | 84 | File::Path::remove_tree($tmpdir); 85 | 86 | return $target_volid; 87 | } 88 | 89 | 1; 90 | -------------------------------------------------------------------------------- /src/PVE/GuestImport/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | install: 3 | install -D -m 0644 OVF.pm ${DESTDIR}${PERLDIR}/PVE/GuestImport/OVF.pm 4 | -------------------------------------------------------------------------------- /src/PVE/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | .PHONY: install 4 | install: 5 | install -D -m 0644 Storage.pm ${DESTDIR}${PERLDIR}/PVE/Storage.pm 6 | install -D -m 0644 Diskmanage.pm ${DESTDIR}${PERLDIR}/PVE/Diskmanage.pm 7 | install -D -m 0644 CephConfig.pm ${DESTDIR}${PERLDIR}/PVE/CephConfig.pm 8 | install -D -m 0644 GuestImport.pm ${DESTDIR}${PERLDIR}/PVE/GuestImport.pm 9 | make -C Storage install 10 | make -C GuestImport install 11 | make -C API2 install 12 | make -C BackupProvider install 13 | make -C CLI install 14 | 15 | .PHONY: test 16 | test: 17 | $(MAKE) -C test test 18 | 19 | clean: 20 | -------------------------------------------------------------------------------- /src/PVE/Storage/CephFSPlugin.pm: -------------------------------------------------------------------------------- 1 | package PVE::Storage::CephFSPlugin; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use IO::File; 7 | use Net::IP; 8 | use File::Path; 9 | 10 | use PVE::CephConfig; 11 | use PVE::JSONSchema qw(get_standard_option); 12 | use PVE::ProcFSTools; 13 | use PVE::Storage::Plugin; 14 | use PVE::Systemd; 15 | use PVE::Tools qw(run_command file_set_contents); 16 | 17 | use base qw(PVE::Storage::Plugin); 18 | 19 | sub cephfs_is_mounted { 20 | my ($scfg, $storeid, $mountdata) = @_; 21 | 22 | my $cmd_option = PVE::CephConfig::ceph_connect_option($scfg, $storeid); 23 | my $configfile = $cmd_option->{ceph_conf}; 24 | 25 | my $subdir = $scfg->{subdir} // '/'; 26 | my $mountpoint = $scfg->{path}; 27 | 28 | $mountdata = PVE::ProcFSTools::parse_proc_mounts() if !$mountdata; 29 | return $mountpoint if grep { 30 | $_->[2] =~ m#^ceph|fuse\.ceph-fuse# && 31 | $_->[0] =~ m#\Q:$subdir\E$|^ceph-fuse$# && 32 | $_->[1] eq $mountpoint 33 | } @$mountdata; 34 | 35 | warn "A filesystem is already mounted on $mountpoint\n" 36 | if grep { $_->[1] eq $mountpoint } @$mountdata; 37 | 38 | return undef; 39 | } 40 | 41 | # FIXME: remove once it's possible to specify _netdev for fuse.ceph mounts 42 | sub systemd_netmount { 43 | my ($where, $type, $what, $opts) = @_; 44 | 45 | # don't do default deps, systemd v241 generator produces ordering deps on both 46 | # local-fs(-pre) and remote-fs(-pre) targets if we use the required _netdev 47 | # option. Over three corners this gets us an ordering cycle on shutdown, which 48 | # may make shutdown hang if the random cycle breaking hits the "wrong" unit to 49 | # delete. 50 | my $unit = <<"EOF"; 51 | [Unit] 52 | Description=${where} 53 | DefaultDependencies=no 54 | Requires=system.slice 55 | Wants=network-online.target 56 | Before=umount.target remote-fs.target 57 | After=systemd-journald.socket system.slice network.target -.mount remote-fs-pre.target network-online.target 58 | Conflicts=umount.target 59 | 60 | [Mount] 61 | Where=${where} 62 | What=${what} 63 | Type=${type} 64 | Options=${opts} 65 | EOF 66 | 67 | my $unit_fn = PVE::Systemd::escape_unit($where, 1) . ".mount"; 68 | my $unit_path = "/run/systemd/system/$unit_fn"; 69 | my $daemon_needs_reload = -e $unit_path; 70 | 71 | file_set_contents($unit_path, $unit); 72 | 73 | run_command(['systemctl', 'daemon-reload'], errmsg => "daemon-reload error") 74 | if $daemon_needs_reload; 75 | run_command(['systemctl', 'start', $unit_fn], errmsg => "mount error"); 76 | 77 | } 78 | 79 | sub cephfs_mount { 80 | my ($scfg, $storeid) = @_; 81 | 82 | my $mountpoint = $scfg->{path}; 83 | my $subdir = $scfg->{subdir} // '/'; 84 | 85 | my $cmd_option = PVE::CephConfig::ceph_connect_option($scfg, $storeid); 86 | my $configfile = $cmd_option->{ceph_conf}; 87 | my $secretfile = $cmd_option->{keyring}; 88 | my $server = $cmd_option->{mon_host} // PVE::CephConfig::get_monaddr_list($configfile); 89 | my $type = 'ceph'; 90 | my $fs_name = $scfg->{'fs-name'}; 91 | 92 | my @opts = (); 93 | if ($scfg->{fuse}) { 94 | $type = 'fuse.ceph'; 95 | push @opts, "ceph.id=$cmd_option->{userid}"; 96 | push @opts, "ceph.keyfile=$secretfile" if defined($secretfile); 97 | push @opts, "ceph.conf=$configfile" if defined($configfile); 98 | push @opts, "ceph.client_fs=$fs_name" if defined($fs_name); 99 | } else { 100 | push @opts, "name=$cmd_option->{userid}"; 101 | push @opts, "secretfile=$secretfile" if defined($secretfile); 102 | push @opts, "conf=$configfile" if defined($configfile); 103 | push @opts, "fs=$fs_name" if defined($fs_name); 104 | } 105 | 106 | push @opts, $scfg->{options} if $scfg->{options}; 107 | 108 | systemd_netmount($mountpoint, $type, "$server:$subdir", join(',', @opts)); 109 | } 110 | 111 | # Configuration 112 | 113 | sub type { 114 | return 'cephfs'; 115 | } 116 | 117 | sub plugindata { 118 | return { 119 | content => [ { vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1 }, 120 | { backup => 1 }], 121 | 'sensitive-properties' => { keyring => 1 }, 122 | }; 123 | } 124 | 125 | sub properties { 126 | return { 127 | fuse => { 128 | description => "Mount CephFS through FUSE.", 129 | type => 'boolean', 130 | }, 131 | 'fs-name' => { 132 | description => "The Ceph filesystem name.", 133 | type => 'string', format => 'pve-configid', 134 | }, 135 | }; 136 | } 137 | 138 | sub options { 139 | return { 140 | path => { fixed => 1 }, 141 | 'content-dirs' => { optional => 1 }, 142 | monhost => { optional => 1}, 143 | nodes => { optional => 1 }, 144 | subdir => { optional => 1 }, 145 | disable => { optional => 1 }, 146 | options => { optional => 1 }, 147 | username => { optional => 1 }, 148 | content => { optional => 1 }, 149 | format => { optional => 1 }, 150 | mkdir => { optional => 1 }, 151 | 'create-base-path' => { optional => 1 }, 152 | 'create-subdirs' => { optional => 1 }, 153 | fuse => { optional => 1 }, 154 | bwlimit => { optional => 1 }, 155 | maxfiles => { optional => 1 }, 156 | keyring => { optional => 1 }, 157 | 'prune-backups' => { optional => 1 }, 158 | 'max-protected-backups' => { optional => 1 }, 159 | 'fs-name' => { optional => 1 }, 160 | }; 161 | } 162 | 163 | sub check_config { 164 | my ($class, $sectionId, $config, $create, $skipSchemaCheck) = @_; 165 | 166 | $config->{path} = "/mnt/pve/$sectionId" if $create && !$config->{path}; 167 | 168 | return $class->SUPER::check_config($sectionId, $config, $create, $skipSchemaCheck); 169 | } 170 | 171 | # Storage implementation 172 | 173 | sub on_add_hook { 174 | my ($class, $storeid, $scfg, %param) = @_; 175 | 176 | PVE::CephConfig::ceph_create_keyfile($scfg->{type}, $storeid, $param{keyring}); 177 | 178 | return; 179 | } 180 | 181 | sub on_update_hook { 182 | my ($class, $storeid, $scfg, %param) = @_; 183 | 184 | if (exists($param{keyring})) { 185 | if (defined($param{keyring})) { 186 | PVE::CephConfig::ceph_create_keyfile($scfg->{type}, $storeid, $param{keyring}); 187 | } else { 188 | PVE::CephConfig::ceph_remove_keyfile($scfg->{type}, $storeid); 189 | } 190 | } 191 | 192 | return; 193 | } 194 | 195 | sub on_delete_hook { 196 | my ($class, $storeid, $scfg) = @_; 197 | PVE::CephConfig::ceph_remove_keyfile($scfg->{type}, $storeid); 198 | return; 199 | } 200 | 201 | sub status { 202 | my ($class, $storeid, $scfg, $cache) = @_; 203 | 204 | $cache->{mountdata} //= PVE::ProcFSTools::parse_proc_mounts(); 205 | 206 | return undef if !cephfs_is_mounted($scfg, $storeid, $cache->{mountdata}); 207 | 208 | return $class->SUPER::status($storeid, $scfg, $cache); 209 | } 210 | 211 | sub activate_storage { 212 | my ($class, $storeid, $scfg, $cache) = @_; 213 | 214 | $cache->{mountdata} //= PVE::ProcFSTools::parse_proc_mounts(); 215 | 216 | # NOTE: mkpath may hang if storage is mounted but not reachable 217 | if (!cephfs_is_mounted($scfg, $storeid, $cache->{mountdata})) { 218 | my $path = $scfg->{path}; 219 | 220 | $class->config_aware_base_mkdir($scfg, $path); 221 | 222 | die "unable to activate storage '$storeid' - " . 223 | "directory '$path' does not exist\n" if ! -d $path; 224 | 225 | cephfs_mount($scfg, $storeid); 226 | } 227 | 228 | $class->SUPER::activate_storage($storeid, $scfg, $cache); 229 | } 230 | 231 | sub deactivate_storage { 232 | my ($class, $storeid, $scfg, $cache) = @_; 233 | 234 | $cache->{mountdata} //= PVE::ProcFSTools::parse_proc_mounts(); 235 | 236 | my $path = $scfg->{path}; 237 | 238 | if (cephfs_is_mounted($scfg, $storeid, $cache->{mountdata})) { 239 | run_command(['/bin/umount', $path], errmsg => 'umount error'); 240 | } 241 | } 242 | 243 | # FIXME remove on the next APIAGE reset. 244 | # Deprecated, use get_volume_attribute instead. 245 | sub get_volume_notes { 246 | my $class = shift; 247 | PVE::Storage::DirPlugin::get_volume_notes($class, @_); 248 | } 249 | 250 | # FIXME remove on the next APIAGE reset. 251 | # Deprecated, use update_volume_attribute instead. 252 | sub update_volume_notes { 253 | my $class = shift; 254 | PVE::Storage::DirPlugin::update_volume_notes($class, @_); 255 | } 256 | 257 | sub get_volume_attribute { 258 | return PVE::Storage::DirPlugin::get_volume_attribute(@_); 259 | } 260 | 261 | sub update_volume_attribute { 262 | return PVE::Storage::DirPlugin::update_volume_attribute(@_); 263 | } 264 | 265 | sub get_import_metadata { 266 | return PVE::Storage::DirPlugin::get_import_metadata(@_); 267 | } 268 | 269 | 1; 270 | -------------------------------------------------------------------------------- /src/PVE/Storage/Common.pm: -------------------------------------------------------------------------------- 1 | package PVE::Storage::Common; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::JSONSchema; 7 | use PVE::Syscall; 8 | 9 | use constant { 10 | FALLOC_FL_KEEP_SIZE => 0x01, # see linux/falloc.h 11 | FALLOC_FL_PUNCH_HOLE => 0x02, # see linux/falloc.h 12 | }; 13 | 14 | =pod 15 | 16 | =head1 NAME 17 | 18 | PVE::Storage::Common - Shared functions and utilities for storage plugins and storage operations 19 | 20 | =head1 DESCRIPTION 21 | 22 | This module contains common subroutines that are mainly to be used by storage 23 | plugins. This module's submodules contain subroutines that are tailored towards 24 | a more specific or related purpose. 25 | 26 | Functions concerned with storage-related C things, helpers 27 | for the C API can be found in this module. Functions that can't 28 | be grouped in a submodule can also be found here. 29 | 30 | =head1 SUBMODULES 31 | 32 | =over 33 | 34 | =back 35 | 36 | =head1 STANDARD OPTIONS FOR JSON SCHEMA 37 | 38 | =over 39 | 40 | =back 41 | 42 | =head3 pve-storage-image-format 43 | 44 | Possible formats a guest image can have. 45 | 46 | =cut 47 | 48 | # TODO PVE 9 - Note that currently, qemu-server allows more formats for VM images, so third party 49 | # storage plugins might potentially allow more too, but none of the plugins we are aware of do that. 50 | # Those formats should either be allowed here or support for them should be phased out (at least in 51 | # the storage layer). Can still be added again in the future, should any plugin provider request it. 52 | 53 | PVE::JSONSchema::register_standard_option('pve-storage-image-format', { 54 | type => 'string', 55 | enum => ['raw', 'qcow2', 'subvol', 'vmdk'], 56 | description => "Format of the image.", 57 | }); 58 | 59 | =pod 60 | 61 | =head1 FUNCTIONS 62 | 63 | =cut 64 | 65 | =pod 66 | 67 | =head3 align_size_up 68 | 69 | $aligned_size = align_size_up($size, $granularity) 70 | 71 | Returns the next size bigger than or equal to C<$size> that is aligned with a 72 | granularity of C<$granularity>. Prints a message if the aligned size is not 73 | equal to the aligned size. 74 | 75 | =cut 76 | 77 | sub align_size_up : prototype($$) { 78 | my ($size, $granularity) = @_; 79 | 80 | my $padding = ($granularity - $size % $granularity) % $granularity; 81 | my $aligned_size = $size + $padding; 82 | print "size $size is not aligned to granularity $granularity, rounding up to $aligned_size\n" 83 | if $aligned_size != $size; 84 | return $aligned_size; 85 | } 86 | 87 | =pod 88 | 89 | =head3 deallocate 90 | 91 | deallocate($file_handle, $offset, $length) 92 | 93 | Deallocates the range with C<$length> many bytes starting from offset C<$offset> 94 | for the file associated to the file handle C<$file_handle>. Dies on failure. 95 | 96 | =cut 97 | 98 | sub deallocate : prototype($$$) { 99 | my ($file_handle, $offset, $length) = @_; 100 | 101 | my $mode = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE; 102 | $offset = int($offset); 103 | $length = int($length); 104 | 105 | if (syscall(PVE::Syscall::fallocate, fileno($file_handle), $mode, $offset, $length) != 0) { 106 | die "fallocate: punch hole failed (offset: $offset, length: $length) - $!\n"; 107 | } 108 | } 109 | 110 | 1; 111 | -------------------------------------------------------------------------------- /src/PVE/Storage/Common/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES = \ 2 | 3 | 4 | .PHONY: install 5 | install: 6 | for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/Storage/Common/$$i; done 7 | -------------------------------------------------------------------------------- /src/PVE/Storage/LunCmd/Comstar.pm: -------------------------------------------------------------------------------- 1 | package PVE::Storage::LunCmd::Comstar; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Digest::MD5 qw(md5_hex); 7 | use PVE::Tools qw(run_command file_read_firstline trim dir_glob_regex dir_glob_foreach); 8 | 9 | my @ssh_opts = ('-o', 'BatchMode=yes'); 10 | my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts); 11 | my $id_rsa_path = '/etc/pve/priv/zfs'; 12 | 13 | my $get_lun_cmd_map = sub { 14 | my ($method) = @_; 15 | 16 | my $stmfadmcmd = "/usr/sbin/stmfadm"; 17 | my $sbdadmcmd = "/usr/sbin/sbdadm"; 18 | 19 | my $cmdmap = { 20 | create_lu => { cmd => $stmfadmcmd, method => 'create-lu' }, 21 | delete_lu => { cmd => $stmfadmcmd, method => 'delete-lu' }, 22 | import_lu => { cmd => $stmfadmcmd, method => 'import-lu' }, 23 | modify_lu => { cmd => $stmfadmcmd, method => 'modify-lu' }, 24 | add_view => { cmd => $stmfadmcmd, method => 'add-view' }, 25 | list_view => { cmd => $stmfadmcmd, method => 'list-view' }, 26 | list_lu => { cmd => $sbdadmcmd, method => 'list-lu' }, 27 | }; 28 | 29 | die "unknown command '$method'" unless exists $cmdmap->{$method}; 30 | 31 | return $cmdmap->{$method}; 32 | }; 33 | 34 | sub get_base { 35 | return '/dev/zvol/rdsk'; 36 | } 37 | 38 | sub run_lun_command { 39 | my ($scfg, $timeout, $method, @params) = @_; 40 | 41 | my $msg = ''; 42 | my $luncmd; 43 | my $target; 44 | my $guid; 45 | $timeout = 10 if !$timeout; 46 | 47 | my $output = sub { 48 | my $line = shift; 49 | $msg .= "$line\n"; 50 | }; 51 | 52 | if ($method eq 'create_lu') { 53 | my $wcd = 'false'; 54 | if ($scfg->{nowritecache}) { 55 | $wcd = 'true'; 56 | } 57 | my $prefix = '600144f'; 58 | my $digest = md5_hex($params[0]); 59 | $digest =~ /(\w{7}(.*))/; 60 | $guid = "$prefix$2"; 61 | @params = ('-p', "wcd=$wcd", '-p', "guid=$guid", @params); 62 | } elsif ($method eq 'modify_lu') { 63 | @params = ('-s', @params); 64 | } elsif ($method eq 'list_view') { 65 | @params = ('-l', @params); 66 | } elsif ($method eq 'list_lu') { 67 | $guid = $params[0]; 68 | @params = undef; 69 | } elsif ($method eq 'add_view') { 70 | if ($scfg->{comstar_tg}) { 71 | unshift @params, $scfg->{comstar_tg}; 72 | unshift @params, '--target-group'; 73 | } 74 | if ($scfg->{comstar_hg}) { 75 | unshift @params, $scfg->{comstar_hg}; 76 | unshift @params, '--host-group'; 77 | } 78 | } 79 | 80 | my $cmdmap = $get_lun_cmd_map->($method); 81 | $luncmd = $cmdmap->{cmd}; 82 | my $lunmethod = $cmdmap->{method}; 83 | 84 | $target = 'root@' . $scfg->{portal}; 85 | 86 | my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target, $luncmd, $lunmethod, @params]; 87 | 88 | run_command($cmd, outfunc => $output, timeout => $timeout); 89 | 90 | if ($method eq 'list_view') { 91 | my @lines = split /\n/, $msg; 92 | $msg = undef; 93 | foreach my $line (@lines) { 94 | if ($line =~ /^\s*LUN\s*:\s*(\d+)$/) { 95 | $msg = $1; 96 | last; 97 | } 98 | } 99 | } elsif ($method eq 'list_lu') { 100 | my $object = $guid; 101 | my @lines = split /\n/, $msg; 102 | $msg = undef; 103 | foreach my $line (@lines) { 104 | if ($line =~ /(\w+)\s+\d+\s+$object$/) { 105 | $msg = $1; 106 | last; 107 | } 108 | } 109 | } elsif ($method eq 'create_lu') { 110 | $msg = $guid; 111 | } 112 | 113 | return $msg; 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/PVE/Storage/LunCmd/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES=Comstar.pm Istgt.pm Iet.pm LIO.pm 2 | 3 | .PHONY: install 4 | install: 5 | for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/Storage/LunCmd/$$i; done 6 | -------------------------------------------------------------------------------- /src/PVE/Storage/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES= \ 2 | Common.pm \ 3 | Plugin.pm \ 4 | DirPlugin.pm \ 5 | LVMPlugin.pm \ 6 | NFSPlugin.pm \ 7 | CIFSPlugin.pm \ 8 | ISCSIPlugin.pm \ 9 | CephFSPlugin.pm \ 10 | RBDPlugin.pm \ 11 | ISCSIDirectPlugin.pm \ 12 | GlusterfsPlugin.pm \ 13 | ZFSPoolPlugin.pm \ 14 | ZFSPlugin.pm \ 15 | PBSPlugin.pm \ 16 | BTRFSPlugin.pm \ 17 | LvmThinPlugin.pm \ 18 | ESXiPlugin.pm 19 | 20 | .PHONY: install 21 | install: 22 | make -C Common install 23 | for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/Storage/$$i; done 24 | make -C LunCmd install 25 | -------------------------------------------------------------------------------- /src/PVE/Storage/NFSPlugin.pm: -------------------------------------------------------------------------------- 1 | package PVE::Storage::NFSPlugin; 2 | 3 | use strict; 4 | use warnings; 5 | use IO::File; 6 | use Net::IP; 7 | use File::Path; 8 | 9 | use PVE::Network; 10 | use PVE::Tools qw(run_command); 11 | use PVE::ProcFSTools; 12 | use PVE::Storage::Plugin; 13 | use PVE::JSONSchema qw(get_standard_option); 14 | 15 | use base qw(PVE::Storage::Plugin); 16 | 17 | # NFS helper functions 18 | 19 | sub nfs_is_mounted { 20 | my ($server, $export, $mountpoint, $mountdata) = @_; 21 | 22 | $server = "[$server]" if Net::IP::ip_is_ipv6($server); 23 | my $source = "$server:$export"; 24 | 25 | $mountdata = PVE::ProcFSTools::parse_proc_mounts() if !$mountdata; 26 | return $mountpoint if grep { 27 | $_->[2] =~ /^nfs/ && 28 | $_->[0] =~ m|^\Q$source\E/?$| && 29 | $_->[1] eq $mountpoint 30 | } @$mountdata; 31 | return undef; 32 | } 33 | 34 | sub nfs_mount { 35 | my ($server, $export, $mountpoint, $options) = @_; 36 | 37 | $server = "[$server]" if Net::IP::ip_is_ipv6($server); 38 | my $source = "$server:$export"; 39 | 40 | my $cmd = ['/bin/mount', '-t', 'nfs', $source, $mountpoint]; 41 | if ($options) { 42 | push @$cmd, '-o', $options; 43 | } 44 | 45 | run_command($cmd, errmsg => "mount error"); 46 | } 47 | 48 | # Configuration 49 | 50 | sub type { 51 | return 'nfs'; 52 | } 53 | 54 | sub plugindata { 55 | return { 56 | content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1 }, 57 | { images => 1 }], 58 | format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ], 59 | 'sensitive-properties' => {}, 60 | }; 61 | } 62 | 63 | sub properties { 64 | return { 65 | export => { 66 | description => "NFS export path.", 67 | type => 'string', format => 'pve-storage-path', 68 | }, 69 | server => { 70 | description => "Server IP or DNS name.", 71 | type => 'string', format => 'pve-storage-server', 72 | }, 73 | }; 74 | } 75 | 76 | sub options { 77 | return { 78 | path => { fixed => 1 }, 79 | 'content-dirs' => { optional => 1 }, 80 | server => { fixed => 1 }, 81 | export => { fixed => 1 }, 82 | nodes => { optional => 1 }, 83 | disable => { optional => 1 }, 84 | maxfiles => { optional => 1 }, 85 | 'prune-backups' => { optional => 1 }, 86 | 'max-protected-backups' => { optional => 1 }, 87 | options => { optional => 1 }, 88 | content => { optional => 1 }, 89 | format => { optional => 1 }, 90 | mkdir => { optional => 1 }, 91 | 'create-base-path' => { optional => 1 }, 92 | 'create-subdirs' => { optional => 1 }, 93 | bwlimit => { optional => 1 }, 94 | preallocation => { optional => 1 }, 95 | }; 96 | } 97 | 98 | 99 | sub check_config { 100 | my ($class, $sectionId, $config, $create, $skipSchemaCheck) = @_; 101 | 102 | $config->{path} = "/mnt/pve/$sectionId" if $create && !$config->{path}; 103 | 104 | return $class->SUPER::check_config($sectionId, $config, $create, $skipSchemaCheck); 105 | } 106 | 107 | # Storage implementation 108 | 109 | sub status { 110 | my ($class, $storeid, $scfg, $cache) = @_; 111 | 112 | $cache->{mountdata} = PVE::ProcFSTools::parse_proc_mounts() 113 | if !$cache->{mountdata}; 114 | 115 | my $path = $scfg->{path}; 116 | my $server = $scfg->{server}; 117 | my $export = $scfg->{export}; 118 | 119 | return undef if !nfs_is_mounted($server, $export, $path, $cache->{mountdata}); 120 | 121 | return $class->SUPER::status($storeid, $scfg, $cache); 122 | } 123 | 124 | sub activate_storage { 125 | my ($class, $storeid, $scfg, $cache) = @_; 126 | 127 | $cache->{mountdata} = PVE::ProcFSTools::parse_proc_mounts() 128 | if !$cache->{mountdata}; 129 | 130 | my $path = $scfg->{path}; 131 | my $server = $scfg->{server}; 132 | my $export = $scfg->{export}; 133 | 134 | if (!nfs_is_mounted($server, $export, $path, $cache->{mountdata})) { 135 | # NOTE: only call mkpath when not mounted (avoid hang when NFS server is offline 136 | $class->config_aware_base_mkdir($scfg, $path); 137 | 138 | die "unable to activate storage '$storeid' - " . 139 | "directory '$path' does not exist\n" if ! -d $path; 140 | 141 | nfs_mount($server, $export, $path, $scfg->{options}); 142 | } 143 | 144 | $class->SUPER::activate_storage($storeid, $scfg, $cache); 145 | } 146 | 147 | sub deactivate_storage { 148 | my ($class, $storeid, $scfg, $cache) = @_; 149 | 150 | $cache->{mountdata} = PVE::ProcFSTools::parse_proc_mounts() 151 | if !$cache->{mountdata}; 152 | 153 | my $path = $scfg->{path}; 154 | my $server = $scfg->{server}; 155 | my $export = $scfg->{export}; 156 | 157 | if (nfs_is_mounted($server, $export, $path, $cache->{mountdata})) { 158 | my $cmd = ['/bin/umount', $path]; 159 | run_command($cmd, errmsg => 'umount error'); 160 | } 161 | } 162 | 163 | sub check_connection { 164 | my ($class, $storeid, $scfg) = @_; 165 | 166 | my $server = $scfg->{server}; 167 | my $opts = $scfg->{options}; 168 | 169 | my $cmd; 170 | 171 | my $is_v4 = defined($opts) && $opts =~ /vers=4.*/; 172 | if ($is_v4) { 173 | my $ip = PVE::JSONSchema::pve_verify_ip($server, 1); 174 | if (!defined($ip)) { 175 | $ip = PVE::Network::get_ip_from_hostname($server); 176 | } 177 | 178 | my $transport = PVE::JSONSchema::pve_verify_ipv4($ip, 1) ? 'tcp' : 'tcp6'; 179 | 180 | # nfsv4 uses a pseudo-filesystem always beginning with / 181 | # no exports are listed 182 | $cmd = ['/usr/sbin/rpcinfo', '-T', $transport, $ip, 'nfs', '4']; 183 | } else { 184 | $cmd = ['/sbin/showmount', '--no-headers', '--exports', $server]; 185 | } 186 | 187 | eval { run_command($cmd, timeout => 10, outfunc => sub {}, errfunc => sub {}) }; 188 | if (my $err = $@) { 189 | if ($is_v4) { 190 | my $port = 2049; 191 | $port = $1 if defined($opts) && $opts =~ /port=(\d+)/; 192 | 193 | # rpcinfo is expected to work when the port is 0 (see 'man 5 nfs') and tcp_ping() 194 | # defaults to port 7 when passing in 0. 195 | return 0 if $port == 0; 196 | 197 | return PVE::Network::tcp_ping($server, $port, 2); 198 | } 199 | return 0; 200 | } 201 | 202 | return 1; 203 | } 204 | 205 | # FIXME remove on the next APIAGE reset. 206 | # Deprecated, use get_volume_attribute instead. 207 | sub get_volume_notes { 208 | my $class = shift; 209 | PVE::Storage::DirPlugin::get_volume_notes($class, @_); 210 | } 211 | 212 | # FIXME remove on the next APIAGE reset. 213 | # Deprecated, use update_volume_attribute instead. 214 | sub update_volume_notes { 215 | my $class = shift; 216 | PVE::Storage::DirPlugin::update_volume_notes($class, @_); 217 | } 218 | 219 | sub get_volume_attribute { 220 | return PVE::Storage::DirPlugin::get_volume_attribute(@_); 221 | } 222 | 223 | sub update_volume_attribute { 224 | return PVE::Storage::DirPlugin::update_volume_attribute(@_); 225 | } 226 | 227 | sub get_import_metadata { 228 | return PVE::Storage::DirPlugin::get_import_metadata(@_); 229 | } 230 | 231 | 1; 232 | -------------------------------------------------------------------------------- /src/PVE/test/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | 3 | .PHONY: test 4 | test: test_ceph_conf_parse_write 5 | 6 | .PHONY: test_ceph_conf_parse_write 7 | test_ceph_conf_parse_write: ceph_conf_parse_write_test.pl 8 | ./ceph_conf_parse_write_test.pl 9 | 10 | -------------------------------------------------------------------------------- /src/bin/Makefile: -------------------------------------------------------------------------------- 1 | DESTDIR= 2 | PREFIX=/usr 3 | SBINDIR=$(PREFIX)/sbin 4 | MANDIR=$(PREFIX)/share/man 5 | MAN1DIR=$(MANDIR)/man1/ 6 | BASHCOMPLDIR=$(PREFIX)/share/bash-completion/completions/ 7 | ZSHCOMPLDIR=$(PREFIX)/share/zsh/vendor-completions/ 8 | 9 | export PERLDIR=$(PREFIX)/share/perl5 10 | 11 | PERL_DOC_INC_DIRS=.. 12 | -include /usr/share/pve-doc-generator/pve-doc-generator.mk 13 | 14 | all: 15 | 16 | pvesm.bash-completion: 17 | perl -I.. -T -e "use PVE::CLI::pvesm; PVE::CLI::pvesm->generate_bash_completions();" >$@.tmp 18 | mv $@.tmp $@ 19 | 20 | pvesm.zsh-completion: 21 | perl -I.. -T -e "use PVE::CLI::pvesm; PVE::CLI::pvesm->generate_zsh_completions();" >$@.tmp 22 | mv $@.tmp $@ 23 | 24 | .PHONY: install 25 | install: pvesm.1 pvesm.bash-completion pvesm.zsh-completion 26 | install -d $(DESTDIR)$(SBINDIR) 27 | install -m 0755 pvesm $(DESTDIR)$(SBINDIR) 28 | install -d $(DESTDIR)$(MAN1DIR) 29 | install -m 0644 pvesm.1 $(DESTDIR)$(MAN1DIR) 30 | gzip -9 -n $(DESTDIR)$(MAN1DIR)/pvesm.1 31 | install -m 0644 -D pvesm.bash-completion $(DESTDIR)$(BASHCOMPLDIR)/pvesm 32 | install -m 0644 -D pvesm.zsh-completion $(DESTDIR)$(ZSHCOMPLDIR)/_pvesm 33 | 34 | .PHONY: clean 35 | clean: 36 | rm -f *.xml.tmp *.1 *.5 *.8 *.adoc docinfo.xml *.bash-completion *.zsh-completion 37 | -------------------------------------------------------------------------------- /src/bin/pvesm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pvesm; 7 | 8 | PVE::CLI::pvesm->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /src/test/Makefile: -------------------------------------------------------------------------------- 1 | all: test 2 | 3 | test: test_zfspoolplugin test_disklist test_bwlimit test_plugin test_ovf 4 | 5 | test_zfspoolplugin: run_test_zfspoolplugin.pl 6 | ./run_test_zfspoolplugin.pl 7 | 8 | test_disklist: run_disk_tests.pl 9 | ./run_disk_tests.pl 10 | 11 | test_bwlimit: run_bwlimit_tests.pl 12 | ./run_bwlimit_tests.pl 13 | 14 | test_plugin: run_plugin_tests.pl 15 | ./run_plugin_tests.pl 16 | 17 | test_ovf: run_ovf_tests.pl 18 | ./run_ovf_tests.pl 19 | -------------------------------------------------------------------------------- /src/test/archive_info_test.pm: -------------------------------------------------------------------------------- 1 | package PVE::Storage::TestArchiveInfo; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib qw(..); 7 | 8 | use PVE::Storage; 9 | use Test::More; 10 | 11 | my $vmid = 16110; 12 | 13 | my $LOG_EXT = PVE::Storage::Plugin::LOG_EXT; 14 | my $NOTES_EXT = PVE::Storage::Plugin::NOTES_EXT; 15 | 16 | # an array of test cases, each test is comprised of the following keys: 17 | # description => to identify a single test 18 | # archive => the input filename for archive_info 19 | # expected => the hash that archive_info returns 20 | # 21 | # most of them are created further below 22 | my $tests = [ 23 | # backup archives 24 | { 25 | description => 'Backup archive, lxc, tgz, future millenium', 26 | archive => "backup/vzdump-lxc-$vmid-3070_01_01-00_00_00.tgz", 27 | expected => { 28 | 'filename' => "vzdump-lxc-$vmid-3070_01_01-00_00_00.tgz", 29 | 'logfilename' => "vzdump-lxc-$vmid-3070_01_01-00_00_00".$LOG_EXT, 30 | 'notesfilename'=> "vzdump-lxc-$vmid-3070_01_01-00_00_00.tgz".$NOTES_EXT, 31 | 'type' => 'lxc', 32 | 'format' => 'tar', 33 | 'decompressor' => ['tar', '-z'], 34 | 'compression' => 'gz', 35 | 'vmid' => $vmid, 36 | 'ctime' => 60*60*24 * (365*1100 + 267), 37 | 'is_std_name' => 1, 38 | }, 39 | }, 40 | { 41 | description => 'Backup archive, lxc, tgz, very old', 42 | archive => "backup/vzdump-lxc-$vmid-1970_01_01-02_00_30.tgz", 43 | expected => { 44 | 'filename' => "vzdump-lxc-$vmid-1970_01_01-02_00_30.tgz", 45 | 'logfilename' => "vzdump-lxc-$vmid-1970_01_01-02_00_30".$LOG_EXT, 46 | 'notesfilename'=> "vzdump-lxc-$vmid-1970_01_01-02_00_30.tgz".$NOTES_EXT, 47 | 'type' => 'lxc', 48 | 'format' => 'tar', 49 | 'decompressor' => ['tar', '-z'], 50 | 'compression' => 'gz', 51 | 'vmid' => $vmid, 52 | 'ctime' => 60*60*2 + 30, 53 | 'is_std_name' => 1, 54 | }, 55 | }, 56 | { 57 | description => 'Backup archive, lxc, tgz', 58 | archive => "backup/vzdump-lxc-$vmid-2020_03_30-21_39_30.tgz", 59 | expected => { 60 | 'filename' => "vzdump-lxc-$vmid-2020_03_30-21_39_30.tgz", 61 | 'logfilename' => "vzdump-lxc-$vmid-2020_03_30-21_39_30".$LOG_EXT, 62 | 'notesfilename'=> "vzdump-lxc-$vmid-2020_03_30-21_39_30.tgz".$NOTES_EXT, 63 | 'type' => 'lxc', 64 | 'format' => 'tar', 65 | 'decompressor' => ['tar', '-z'], 66 | 'compression' => 'gz', 67 | 'vmid' => $vmid, 68 | 'ctime' => 1585604370, 69 | 'is_std_name' => 1, 70 | }, 71 | }, 72 | { 73 | description => 'Backup archive, openvz, tgz', 74 | archive => "backup/vzdump-openvz-$vmid-2020_03_30-21_39_30.tgz", 75 | expected => { 76 | 'filename' => "vzdump-openvz-$vmid-2020_03_30-21_39_30.tgz", 77 | 'logfilename' => "vzdump-openvz-$vmid-2020_03_30-21_39_30".$LOG_EXT, 78 | 'notesfilename'=> "vzdump-openvz-$vmid-2020_03_30-21_39_30.tgz".$NOTES_EXT, 79 | 'type' => 'openvz', 80 | 'format' => 'tar', 81 | 'decompressor' => ['tar', '-z'], 82 | 'compression' => 'gz', 83 | 'vmid' => $vmid, 84 | 'ctime' => 1585604370, 85 | 'is_std_name' => 1, 86 | }, 87 | }, 88 | { 89 | description => 'Backup archive, custom dump directory, qemu, tgz', 90 | archive => "/here/be/Back-ups/vzdump-qemu-$vmid-2020_03_30-21_39_30.tgz", 91 | expected => { 92 | 'filename' => "vzdump-qemu-$vmid-2020_03_30-21_39_30.tgz", 93 | 'logfilename' => "vzdump-qemu-$vmid-2020_03_30-21_39_30".$LOG_EXT, 94 | 'notesfilename'=> "vzdump-qemu-$vmid-2020_03_30-21_39_30.tgz".$NOTES_EXT, 95 | 'type' => 'qemu', 96 | 'format' => 'tar', 97 | 'decompressor' => ['tar', '-z'], 98 | 'compression' => 'gz', 99 | 'vmid' => $vmid, 100 | 'ctime' => 1585604370, 101 | 'is_std_name' => 1, 102 | }, 103 | }, 104 | { 105 | description => 'Backup archive, none, tgz', 106 | archive => "backup/vzdump-qemu-$vmid-whatever-the-name_is_here.tgz", 107 | expected => { 108 | 'filename' => "vzdump-qemu-$vmid-whatever-the-name_is_here.tgz", 109 | 'type' => 'qemu', 110 | 'format' => 'tar', 111 | 'decompressor' => ['tar', '-z'], 112 | 'compression' => 'gz', 113 | 'is_std_name' => 0, 114 | }, 115 | }, 116 | ]; 117 | 118 | # add new compression fromats to test 119 | my $decompressor = { 120 | tar => { 121 | gz => ['tar', '-z'], 122 | lzo => ['tar', '--lzop'], 123 | zst => ['tar', '--zstd'], 124 | bz2 => ['tar', '--bzip2'], 125 | }, 126 | vma => { 127 | gz => ['zcat'], 128 | lzo => ['lzop', '-d', '-c'], 129 | zst => ['zstd', '-q', '-d', '-c'], 130 | bz2 => ['bzcat', '-q'], 131 | }, 132 | }; 133 | 134 | my $bkp_suffix = { 135 | qemu => [ 'vma', $decompressor->{vma}, ], 136 | lxc => [ 'tar', $decompressor->{tar}, ], 137 | openvz => [ 'tar', $decompressor->{tar}, ], 138 | }; 139 | 140 | # create more test cases for backup files matches 141 | for my $virt (sort keys %$bkp_suffix) { 142 | my ($format, $decomp) = $bkp_suffix->{$virt}->@*; 143 | my $archive_name = "vzdump-$virt-$vmid-2020_03_30-21_12_40"; 144 | 145 | for my $suffix (sort keys %$decomp) { 146 | push @$tests, { 147 | description => "Backup archive, $virt, $format.$suffix", 148 | archive => "backup/$archive_name.$format.$suffix", 149 | expected => { 150 | 'filename' => "$archive_name.$format.$suffix", 151 | 'logfilename' => $archive_name.$LOG_EXT, 152 | 'notesfilename'=> "$archive_name.$format.$suffix".$NOTES_EXT, 153 | 'type' => "$virt", 154 | 'format' => "$format", 155 | 'decompressor' => $decomp->{$suffix}, 156 | 'compression' => "$suffix", 157 | 'vmid' => $vmid, 158 | 'ctime' => 1585602760, 159 | 'is_std_name' => 1, 160 | }, 161 | }; 162 | } 163 | } 164 | 165 | 166 | # add compression formats to test failed matches 167 | my $non_bkp_suffix = { 168 | 'openvz' => [ 'zip', 'tgz.lzo', 'zip.gz', '', ], 169 | 'lxc' => [ 'zip', 'tgz.lzo', 'zip.gz', '', ], 170 | 'qemu' => [ 'vma.xz', 'vms.gz', 'vmx.zst', '', ], 171 | 'none' => [ 'tar.gz', ], 172 | }; 173 | 174 | # create tests for failed matches 175 | for my $virt (sort keys %$non_bkp_suffix) { 176 | my $suffix = $non_bkp_suffix->{$virt}; 177 | for my $s (@$suffix) { 178 | my $archive = "backup/vzdump-$virt-$vmid-2020_03_30-21_12_40.$s"; 179 | push @$tests, { 180 | description => "Failed match: Backup archive, $virt, $s", 181 | archive => $archive, 182 | expected => "ERROR: couldn't determine archive info from '$archive'\n", 183 | }; 184 | } 185 | } 186 | 187 | 188 | plan tests => scalar @$tests; 189 | 190 | for my $tt (@$tests) { 191 | 192 | my $got = eval { PVE::Storage::archive_info($tt->{archive}) }; 193 | $got = $@ if $@; 194 | 195 | is_deeply($got, $tt->{expected}, $tt->{description}) || diag(explain($got)); 196 | } 197 | 198 | done_testing(); 199 | 200 | 1; 201 | -------------------------------------------------------------------------------- /src/test/disk_tests/cciss/cciss!c0d0/device/model: -------------------------------------------------------------------------------- 1 | LOGICAL_VOLUME 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/cciss/cciss!c0d0/device/vendor: -------------------------------------------------------------------------------- 1 | HP 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/cciss/cciss!c0d0/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/cciss/cciss!c0d0/size: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/cciss/cciss!c0d0_udevadm: -------------------------------------------------------------------------------- 1 | P: /devices/pci0000:40/0000:40:13.0/0000:45:00.0/cciss0/c0d0/block/cciss!c0d0 2 | N: cciss/c0d0 3 | S: disk/by-id/cciss-SERIAL111 4 | S: disk/by-id/wwn-0x00000000000000000000000000000000 5 | S: disk/by-path/pci-0000:45:00.0-cciss-disk0 6 | E: DEVLINKS=/dev/disk/by-id/cciss-000000000000000000000000000000000 /dev/disk/by-id/wwn-0x000000000000000000000000000000000/dev/disk/by-path/pci-0000:45:00.0-cciss-disk0 7 | E: DEVNAME=/dev/cciss/c0d0 8 | E: DEVPATH=/devices/pci0000:40/0000:40:13.0/0000:45:00.0/cciss0/c0d0/block/cciss!c0d0 9 | E: DEVTYPE=disk 10 | E: ID_BUS=cciss 11 | E: ID_MODEL=LOGICAL_VOLUME 12 | E: ID_MODEL_ENC=LOGICAL\x20VOLUME\x20\x20 13 | E: ID_PART_TABLE_TYPE=gpt 14 | E: ID_PART_TABLE_UUID=cfe72deb-65d1-487c-bdfa-8af66dc1a969 15 | E: ID_PATH=pci-0000:45:00.0-cciss-disk0 16 | E: ID_PATH_TAG=pci-0000_45_00_0-cciss-disk0 17 | E: ID_REVISION=7.24 18 | E: ID_SCSI=1 19 | E: ID_SCSI_SERIAL=SERIAL1 20 | E: ID_SERIAL=SERIAL111 21 | E: ID_SERIAL_SHORT=SER111 22 | E: ID_TYPE=disk 23 | E: ID_VENDOR=HP 24 | E: ID_VENDOR_ENC=HP\x20\x20\x20\x20\x20\x20 25 | E: ID_WWN=0x0000000000000000 26 | E: ID_WWN_VENDOR_EXTENSION=0x0000000000000000 27 | E: ID_WWN_WITH_EXTENSION=0x00000000000000000000000000000000 28 | E: MAJOR=104 29 | E: MINOR=0 30 | E: SUBSYSTEM=block 31 | E: TAGS=:systemd: 32 | E: USEC_INITIALIZED=2247 33 | -------------------------------------------------------------------------------- /src/test/disk_tests/cciss/disklist: -------------------------------------------------------------------------------- 1 | cciss!c0d0 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/cciss/disklist_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "cciss/c0d0" : { 3 | "wearout" : "N/A", 4 | "vendor" : "HP", 5 | "rpm" : -1, 6 | "type" : "unknown", 7 | "serial" : "SER111", 8 | "osdid" : -1, 9 | "osdid-list" : null, 10 | "health" : "UNKNOWN", 11 | "model" : "LOGICAL_VOLUME", 12 | "size" : 5120, 13 | "wwn" : "0x0000000000000000", 14 | "gpt" : 1, 15 | "devpath" : "/dev/cciss/c0d0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/disklist: -------------------------------------------------------------------------------- 1 | sda 2 | sdb 3 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/disklist_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdb" : { 3 | "devpath" : "/dev/sdb", 4 | "size" : 1024000, 5 | "gpt" : 1, 6 | "osdid" : -1, 7 | "osdid-list" : null, 8 | "rpm" : 7200, 9 | "model" : "ST4000NM0033-9ZM170", 10 | "vendor" : "ATA", 11 | "health" : "PASSED", 12 | "type" : "hdd", 13 | "wwn" : "0x0000000000000000", 14 | "wearout" : "N/A", 15 | "serial" : "00000000" 16 | }, 17 | "sda" : { 18 | "osdid" : -1, 19 | "osdid-list" : null, 20 | "size" : 1024000, 21 | "gpt" : 1, 22 | "devpath" : "/dev/sda", 23 | "model" : "ST4000DM000-1F2168", 24 | "rpm" : 5900, 25 | "type" : "hdd", 26 | "health" : "PASSED", 27 | "vendor" : "ATA", 28 | "serial" : "00000000", 29 | "wearout" : "N/A", 30 | "wwn" : "0x0000000000000000" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sda/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sda/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sda/size: -------------------------------------------------------------------------------- 1 | 2000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sda_health: -------------------------------------------------------------------------------- 1 | smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.4.21-1-pve] (local build) 2 | Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sda_smart: -------------------------------------------------------------------------------- 1 | smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.4.21-1-pve] (local build) 2 | Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | 7 | SMART Attributes Data Structure revision number: 10 8 | Vendor Specific SMART Attributes with Thresholds: 9 | ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 10 | 1 Raw_Read_Error_Rate POSR-- 117 099 006 - 158983656 11 | 3 Spin_Up_Time PO---- 092 091 000 - 0 12 | 4 Start_Stop_Count -O--CK 100 100 020 - 189 13 | 5 Reallocated_Sector_Ct PO--CK 100 100 010 - 0 14 | 7 Seek_Error_Rate POSR-- 075 060 030 - 30779387 15 | 9 Power_On_Hours -O--CK 099 099 000 - 1250 16 | 10 Spin_Retry_Count PO--C- 100 100 097 - 0 17 | 12 Power_Cycle_Count -O--CK 100 100 020 - 190 18 | 183 Runtime_Bad_Block -O--CK 100 100 000 - 0 19 | 184 End-to-End_Error -O--CK 100 100 099 - 0 20 | 187 Reported_Uncorrect -O--CK 100 100 000 - 0 21 | 188 Command_Timeout -O--CK 100 100 000 - 0 0 0 22 | 189 High_Fly_Writes -O-RCK 100 100 000 - 0 23 | 190 Airflow_Temperature_Cel -O---K 069 061 045 - 31 (Min/Max 20/33) 24 | 191 G-Sense_Error_Rate -O--CK 100 100 000 - 0 25 | 192 Power-Off_Retract_Count -O--CK 100 100 000 - 43 26 | 193 Load_Cycle_Count -O--CK 100 100 000 - 201 27 | 194 Temperature_Celsius -O---K 031 040 000 - 31 (0 17 0 0 0) 28 | 197 Current_Pending_Sector -O--C- 100 100 000 - 0 29 | 198 Offline_Uncorrectable ----C- 100 100 000 - 0 30 | 199 UDMA_CRC_Error_Count -OSRCK 200 200 000 - 0 31 | 240 Head_Flying_Hours ------ 100 253 000 - 1259h+06m+33.546s 32 | 241 Total_LBAs_Written ------ 100 253 000 - 24013587236 33 | 242 Total_LBAs_Read ------ 100 253 000 - 66916845706732 34 | ||||||_ K auto-keep 35 | |||||__ C event count 36 | ||||___ R error rate 37 | |||____ S speed/performance 38 | ||_____ O updated online 39 | |______ P prefailure warning 40 | 41 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sda_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes" : [ 3 | { 4 | "threshold" : 6, 5 | "fail" : "-", 6 | "flags" : "POSR--", 7 | "normalized" : 117, 8 | "value" : 117, 9 | "id" : " 1", 10 | "raw" : "158983656", 11 | "name" : "Raw_Read_Error_Rate", 12 | "worst" : 99 13 | }, 14 | { 15 | "flags" : "PO----", 16 | "normalized" : 92, 17 | "value" : 92, 18 | "raw" : "0", 19 | "name" : "Spin_Up_Time", 20 | "worst" : 91, 21 | "id" : " 3", 22 | "fail" : "-", 23 | "threshold" : 0 24 | }, 25 | { 26 | "normalized" : 100, 27 | "value" : 100, 28 | "flags" : "-O--CK", 29 | "id" : " 4", 30 | "name" : "Start_Stop_Count", 31 | "worst" : 100, 32 | "raw" : "189", 33 | "threshold" : 20, 34 | "fail" : "-" 35 | }, 36 | { 37 | "normalized" : 100, 38 | "value" : 100, 39 | "flags" : "PO--CK", 40 | "id" : " 5", 41 | "name" : "Reallocated_Sector_Ct", 42 | "worst" : 100, 43 | "raw" : "0", 44 | "threshold" : 10, 45 | "fail" : "-" 46 | }, 47 | { 48 | "flags" : "POSR--", 49 | "normalized" : 75, 50 | "value" : 75, 51 | "raw" : "30779387", 52 | "worst" : 60, 53 | "name" : "Seek_Error_Rate", 54 | "id" : " 7", 55 | "fail" : "-", 56 | "threshold" : 30 57 | }, 58 | { 59 | "raw" : "1250", 60 | "worst" : 99, 61 | "name" : "Power_On_Hours", 62 | "id" : " 9", 63 | "flags" : "-O--CK", 64 | "normalized" : 99, 65 | "value" : 99, 66 | "fail" : "-", 67 | "threshold" : 0 68 | }, 69 | { 70 | "normalized" : 100, 71 | "value" : 100, 72 | "flags" : "PO--C-", 73 | "id" : " 10", 74 | "name" : "Spin_Retry_Count", 75 | "worst" : 100, 76 | "raw" : "0", 77 | "threshold" : 97, 78 | "fail" : "-" 79 | }, 80 | { 81 | "flags" : "-O--CK", 82 | "normalized" : 100, 83 | "value" : 100, 84 | "id" : " 12", 85 | "raw" : "190", 86 | "worst" : 100, 87 | "name" : "Power_Cycle_Count", 88 | "threshold" : 20, 89 | "fail" : "-" 90 | }, 91 | { 92 | "threshold" : 0, 93 | "fail" : "-", 94 | "flags" : "-O--CK", 95 | "normalized" : 100, 96 | "value" : 100, 97 | "id" : "183", 98 | "raw" : "0", 99 | "worst" : 100, 100 | "name" : "Runtime_Bad_Block" 101 | }, 102 | { 103 | "fail" : "-", 104 | "threshold" : 99, 105 | "flags" : "-O--CK", 106 | "normalized" : 100, 107 | "value" : 100, 108 | "raw" : "0", 109 | "worst" : 100, 110 | "name" : "End-to-End_Error", 111 | "id" : "184" 112 | }, 113 | { 114 | "worst" : 100, 115 | "name" : "Reported_Uncorrect", 116 | "raw" : "0", 117 | "id" : "187", 118 | "normalized" : 100, 119 | "value" : 100, 120 | "flags" : "-O--CK", 121 | "fail" : "-", 122 | "threshold" : 0 123 | }, 124 | { 125 | "flags" : "-O--CK", 126 | "normalized" : 100, 127 | "value" : 100, 128 | "raw" : "0 0 0", 129 | "worst" : 100, 130 | "name" : "Command_Timeout", 131 | "id" : "188", 132 | "fail" : "-", 133 | "threshold" : 0 134 | }, 135 | { 136 | "threshold" : 0, 137 | "fail" : "-", 138 | "flags" : "-O-RCK", 139 | "normalized" : 100, 140 | "value" : 100, 141 | "id" : "189", 142 | "raw" : "0", 143 | "name" : "High_Fly_Writes", 144 | "worst" : 100 145 | }, 146 | { 147 | "worst" : 61, 148 | "name" : "Airflow_Temperature_Cel", 149 | "raw" : "31 (Min/Max 20/33)", 150 | "id" : "190", 151 | "normalized" : 69, 152 | "value" : 69, 153 | "flags" : "-O---K", 154 | "fail" : "-", 155 | "threshold" : 45 156 | }, 157 | { 158 | "fail" : "-", 159 | "threshold" : 0, 160 | "worst" : 100, 161 | "name" : "G-Sense_Error_Rate", 162 | "raw" : "0", 163 | "id" : "191", 164 | "normalized" : 100, 165 | "value" : 100, 166 | "flags" : "-O--CK" 167 | }, 168 | { 169 | "id" : "192", 170 | "raw" : "43", 171 | "name" : "Power-Off_Retract_Count", 172 | "worst" : 100, 173 | "flags" : "-O--CK", 174 | "normalized" : 100, 175 | "value" : 100, 176 | "threshold" : 0, 177 | "fail" : "-" 178 | }, 179 | { 180 | "flags" : "-O--CK", 181 | "normalized" : 100, 182 | "value" : 100, 183 | "raw" : "201", 184 | "worst" : 100, 185 | "name" : "Load_Cycle_Count", 186 | "id" : "193", 187 | "fail" : "-", 188 | "threshold" : 0 189 | }, 190 | { 191 | "fail" : "-", 192 | "threshold" : 0, 193 | "normalized" : 31, 194 | "value" : 31, 195 | "flags" : "-O---K", 196 | "name" : "Temperature_Celsius", 197 | "worst" : 40, 198 | "raw" : "31 (0 17 0 0 0)", 199 | "id" : "194" 200 | }, 201 | { 202 | "normalized" : 100, 203 | "value" : 100, 204 | "flags" : "-O--C-", 205 | "id" : "197", 206 | "worst" : 100, 207 | "name" : "Current_Pending_Sector", 208 | "raw" : "0", 209 | "threshold" : 0, 210 | "fail" : "-" 211 | }, 212 | { 213 | "worst" : 100, 214 | "name" : "Offline_Uncorrectable", 215 | "raw" : "0", 216 | "id" : "198", 217 | "normalized" : 100, 218 | "value" : 100, 219 | "flags" : "----C-", 220 | "fail" : "-", 221 | "threshold" : 0 222 | }, 223 | { 224 | "threshold" : 0, 225 | "fail" : "-", 226 | "normalized" : 200, 227 | "value" : 200, 228 | "flags" : "-OSRCK", 229 | "id" : "199", 230 | "worst" : 200, 231 | "name" : "UDMA_CRC_Error_Count", 232 | "raw" : "0" 233 | }, 234 | { 235 | "raw" : "1259h+06m+33.546s", 236 | "name" : "Head_Flying_Hours", 237 | "worst" : 253, 238 | "id" : "240", 239 | "flags" : "------", 240 | "normalized" : 100, 241 | "value" : 100, 242 | "fail" : "-", 243 | "threshold" : 0 244 | }, 245 | { 246 | "fail" : "-", 247 | "threshold" : 0, 248 | "name" : "Total_LBAs_Written", 249 | "worst" : 253, 250 | "raw" : "24013587236", 251 | "id" : "241", 252 | "normalized" : 100, 253 | "value" : 100, 254 | "flags" : "------" 255 | }, 256 | { 257 | "flags" : "------", 258 | "normalized" : 100, 259 | "value" : 100, 260 | "id" : "242", 261 | "raw" : "66916845706732", 262 | "worst" : 253, 263 | "name" : "Total_LBAs_Read", 264 | "threshold" : 0, 265 | "fail" : "-" 266 | } 267 | ], 268 | "health" : "PASSED", 269 | "type" : "ata" 270 | } 271 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sda_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sda 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=5900 4 | E: ID_BUS=ata 5 | E: ID_MODEL=ST4000DM000-1F2168 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=4f2e07a6-5437-2b4e-b6e8-9cba98639324 8 | E: ID_SERIAL_SHORT=00000000 9 | E: ID_TYPE=disk 10 | E: ID_WWN=0x0000000000000000 11 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 12 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sdb/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sdb/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sdb/size: -------------------------------------------------------------------------------- 1 | 2000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sdb_health: -------------------------------------------------------------------------------- 1 | smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.4.21-1-pve] (local build) 2 | Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sdb_smart: -------------------------------------------------------------------------------- 1 | smartctl 6.4 2014-10-07 r4002 [x86_64-linux-4.4.10-1-pve] (local build) 2 | Copyright (C) 2002-14, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | 7 | SMART Attributes Data Structure revision number: 10 8 | Vendor Specific SMART Attributes with Thresholds: 9 | ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH FAIL RAW_VALUE 10 | 1 Raw_Read_Error_Rate POSR-- 083 063 044 - 215697022 11 | 3 Spin_Up_Time PO---- 092 091 000 - 0 12 | 4 Start_Stop_Count -O--CK 100 100 020 - 265 13 | 5 Reallocated_Sector_Ct PO--CK 100 100 010 - 0 14 | 7 Seek_Error_Rate POSR-- 091 060 030 - 1572375006 15 | 9 Power_On_Hours -O--CK 089 089 000 - 9885 16 | 10 Spin_Retry_Count PO--C- 100 100 097 - 0 17 | 12 Power_Cycle_Count -O--CK 100 100 020 - 265 18 | 184 End-to-End_Error -O--CK 100 100 099 - 0 19 | 187 Reported_Uncorrect -O--CK 100 100 000 - 0 20 | 188 Command_Timeout -O--CK 100 100 000 - 0 21 | 189 High_Fly_Writes -O--CK 100 100 000 - 0 22 | 190 Airflow_Temperature_Cel -O-RCK 045 036 045 NOW 55 (147 229 55 24 0) 23 | 191 G-Sense_Error_Rate -O---K 100 100 000 - 0 24 | 192 Power-Off_Retract_Count -O--CK 100 100 000 - 57 25 | 193 Load_Cycle_Count -O--CK 100 100 000 - 265 26 | 194 Temperature_Celsius -O--CK 055 064 000 - 55 (0 16 0 0 0) 27 | 195 Hardware_ECC_Recovered -O---K 023 013 000 - 215697022 28 | 197 Current_Pending_Sector -O--C- 100 100 000 - 0 29 | 198 Offline_Uncorrectable ----C- 100 100 000 - 0 30 | 199 UDMA_CRC_Error_Count -OSRCK 200 200 000 - 0 31 | ||||||_ K auto-keep 32 | |||||__ C event count 33 | ||||___ R error rate 34 | |||____ S speed/performance 35 | ||_____ O updated online 36 | |______ P prefailure warning 37 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sdb_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes" : [ 3 | { 4 | "threshold" : 44, 5 | "fail" : "-", 6 | "id" : " 1", 7 | "name" : "Raw_Read_Error_Rate", 8 | "worst" : 63, 9 | "raw" : "215697022", 10 | "normalized" : 83, 11 | "value" : 83, 12 | "flags" : "POSR--" 13 | }, 14 | { 15 | "flags" : "PO----", 16 | "normalized" : 92, 17 | "value" : 92, 18 | "id" : " 3", 19 | "raw" : "0", 20 | "worst" : 91, 21 | "name" : "Spin_Up_Time", 22 | "threshold" : 0, 23 | "fail" : "-" 24 | }, 25 | { 26 | "fail" : "-", 27 | "threshold" : 20, 28 | "normalized" : 100, 29 | "value" : 100, 30 | "flags" : "-O--CK", 31 | "worst" : 100, 32 | "name" : "Start_Stop_Count", 33 | "raw" : "265", 34 | "id" : " 4" 35 | }, 36 | { 37 | "flags" : "PO--CK", 38 | "normalized" : 100, 39 | "value" : 100, 40 | "id" : " 5", 41 | "raw" : "0", 42 | "name" : "Reallocated_Sector_Ct", 43 | "worst" : 100, 44 | "threshold" : 10, 45 | "fail" : "-" 46 | }, 47 | { 48 | "id" : " 7", 49 | "raw" : "1572375006", 50 | "name" : "Seek_Error_Rate", 51 | "worst" : 60, 52 | "flags" : "POSR--", 53 | "normalized" : 91, 54 | "value" : 91, 55 | "threshold" : 30, 56 | "fail" : "-" 57 | }, 58 | { 59 | "raw" : "9885", 60 | "name" : "Power_On_Hours", 61 | "worst" : 89, 62 | "id" : " 9", 63 | "flags" : "-O--CK", 64 | "normalized" : 89, 65 | "value" : 89, 66 | "fail" : "-", 67 | "threshold" : 0 68 | }, 69 | { 70 | "fail" : "-", 71 | "threshold" : 97, 72 | "normalized" : 100, 73 | "value" : 100, 74 | "flags" : "PO--C-", 75 | "worst" : 100, 76 | "name" : "Spin_Retry_Count", 77 | "raw" : "0", 78 | "id" : " 10" 79 | }, 80 | { 81 | "threshold" : 20, 82 | "fail" : "-", 83 | "id" : " 12", 84 | "raw" : "265", 85 | "name" : "Power_Cycle_Count", 86 | "worst" : 100, 87 | "flags" : "-O--CK", 88 | "normalized" : 100, 89 | "value" : 100 90 | }, 91 | { 92 | "name" : "End-to-End_Error", 93 | "worst" : 100, 94 | "raw" : "0", 95 | "id" : "184", 96 | "normalized" : 100, 97 | "value" : 100, 98 | "flags" : "-O--CK", 99 | "fail" : "-", 100 | "threshold" : 99 101 | }, 102 | { 103 | "threshold" : 0, 104 | "fail" : "-", 105 | "id" : "187", 106 | "raw" : "0", 107 | "name" : "Reported_Uncorrect", 108 | "worst" : 100, 109 | "flags" : "-O--CK", 110 | "normalized" : 100, 111 | "value" : 100 112 | }, 113 | { 114 | "threshold" : 0, 115 | "fail" : "-", 116 | "id" : "188", 117 | "raw" : "0", 118 | "name" : "Command_Timeout", 119 | "worst" : 100, 120 | "flags" : "-O--CK", 121 | "normalized" : 100, 122 | "value" : 100 123 | }, 124 | { 125 | "fail" : "-", 126 | "threshold" : 0, 127 | "raw" : "0", 128 | "worst" : 100, 129 | "name" : "High_Fly_Writes", 130 | "id" : "189", 131 | "flags" : "-O--CK", 132 | "normalized" : 100, 133 | "value" : 100 134 | }, 135 | { 136 | "flags" : "-O-RCK", 137 | "normalized" : 45, 138 | "value" : 45, 139 | "raw" : "55 (147 229 55 24 0)", 140 | "worst" : 36, 141 | "name" : "Airflow_Temperature_Cel", 142 | "id" : "190", 143 | "fail" : "NOW", 144 | "threshold" : 45 145 | }, 146 | { 147 | "fail" : "-", 148 | "threshold" : 0, 149 | "raw" : "0", 150 | "worst" : 100, 151 | "name" : "G-Sense_Error_Rate", 152 | "id" : "191", 153 | "flags" : "-O---K", 154 | "normalized" : 100, 155 | "value" : 100 156 | }, 157 | { 158 | "threshold" : 0, 159 | "fail" : "-", 160 | "id" : "192", 161 | "raw" : "57", 162 | "worst" : 100, 163 | "name" : "Power-Off_Retract_Count", 164 | "flags" : "-O--CK", 165 | "normalized" : 100, 166 | "value" : 100 167 | }, 168 | { 169 | "fail" : "-", 170 | "threshold" : 0, 171 | "normalized" : 100, 172 | "value" : 100, 173 | "flags" : "-O--CK", 174 | "name" : "Load_Cycle_Count", 175 | "worst" : 100, 176 | "raw" : "265", 177 | "id" : "193" 178 | }, 179 | { 180 | "threshold" : 0, 181 | "fail" : "-", 182 | "flags" : "-O--CK", 183 | "normalized" : 55, 184 | "value" : 55, 185 | "id" : "194", 186 | "raw" : "55 (0 16 0 0 0)", 187 | "name" : "Temperature_Celsius", 188 | "worst" : 64 189 | }, 190 | { 191 | "threshold" : 0, 192 | "fail" : "-", 193 | "id" : "195", 194 | "name" : "Hardware_ECC_Recovered", 195 | "worst" : 13, 196 | "raw" : "215697022", 197 | "normalized" : 23, 198 | "value" : 23, 199 | "flags" : "-O---K" 200 | }, 201 | { 202 | "worst" : 100, 203 | "name" : "Current_Pending_Sector", 204 | "raw" : "0", 205 | "id" : "197", 206 | "normalized" : 100, 207 | "value" : 100, 208 | "flags" : "-O--C-", 209 | "fail" : "-", 210 | "threshold" : 0 211 | }, 212 | { 213 | "worst" : 100, 214 | "name" : "Offline_Uncorrectable", 215 | "raw" : "0", 216 | "id" : "198", 217 | "normalized" : 100, 218 | "value" : 100, 219 | "flags" : "----C-", 220 | "fail" : "-", 221 | "threshold" : 0 222 | }, 223 | { 224 | "fail" : "-", 225 | "threshold" : 0, 226 | "normalized" : 200, 227 | "value" : 200, 228 | "flags" : "-OSRCK", 229 | "worst" : 200, 230 | "name" : "UDMA_CRC_Error_Count", 231 | "raw" : "0", 232 | "id" : "199" 233 | } 234 | ], 235 | "type" : "ata", 236 | "health" : "PASSED" 237 | } 238 | -------------------------------------------------------------------------------- /src/test/disk_tests/hdd_smart/sdb_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdb 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=7200 4 | E: ID_BUS=ata 5 | E: ID_MODEL=ST4000NM0033-9ZM170 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=4f2e07a6-5437-2b4e-b6e8-9cba98639324 8 | E: ID_SERIAL_SHORT=00000000 9 | E: ID_TYPE=disk 10 | E: ID_WWN=0x0000000000000000 11 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 12 | -------------------------------------------------------------------------------- /src/test/disk_tests/nvme_smart/disklist: -------------------------------------------------------------------------------- 1 | nvme0n1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/nvme_smart/disklist_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "nvme0n1" : { 3 | "wearout" : 69, 4 | "vendor" : "unknown", 5 | "size" : 512000, 6 | "health" : "PASSED", 7 | "serial" : "unknown", 8 | "model" : "NVME MODEL 1", 9 | "rpm" : 0, 10 | "osdid" : -1, 11 | "osdid-list" : null, 12 | "devpath" : "/dev/nvme0n1", 13 | "gpt" : 0, 14 | "wwn" : "unknown", 15 | "type" : "nvme" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/disk_tests/nvme_smart/nvme0n1/device/model: -------------------------------------------------------------------------------- 1 | NVME MODEL 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/nvme_smart/nvme0n1/queue/rotational: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/nvme_smart/nvme0n1/size: -------------------------------------------------------------------------------- 1 | 1000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/nvme_smart/nvme0n1_smart: -------------------------------------------------------------------------------- 1 | smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.4.19-1-pve] (local build) 2 | Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | 7 | SMART/Health Information (NVMe Log 0x02, NSID 0xffffffff) 8 | Critical Warning: 0x00 9 | Temperature: 32 Celsius 10 | Available Spare: 100% 11 | Available Spare Threshold: 10% 12 | Percentage Used: 31% 13 | Data Units Read: 1,299,288 [665 GB] 14 | Data Units Written: 5,592,478 [2.86 TB] 15 | Host Read Commands: 30,360,807 16 | Host Write Commands: 470,356,196 17 | Controller Busy Time: 12 18 | Power Cycles: 98 19 | Power On Hours: 687 20 | Unsafe Shutdowns: 21 21 | Media and Data Integrity Errors: 0 22 | Error Information Log Entries: 0 23 | -------------------------------------------------------------------------------- /src/test/disk_tests/nvme_smart/nvme0n1_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "text" : "\nSMART/Health Information (NVMe Log 0x02, NSID 0xffffffff)\nCritical Warning: 0x00\nTemperature: 32 Celsius\nAvailable Spare: 100%\nAvailable Spare Threshold: 10%\nPercentage Used: 31%\nData Units Read: 1,299,288 [665 GB]\nData Units Written: 5,592,478 [2.86 TB]\nHost Read Commands: 30,360,807\nHost Write Commands: 470,356,196\nController Busy Time: 12\nPower Cycles: 98\nPower On Hours: 687\nUnsafe Shutdowns: 21\nMedia and Data Integrity Errors: 0\nError Information Log Entries: 0\n", 3 | "health" : "PASSED", 4 | "type" : "text", 5 | "wearout": 69 6 | } 7 | -------------------------------------------------------------------------------- /src/test/disk_tests/nvme_smart/nvme0n1_udevadm: -------------------------------------------------------------------------------- 1 | 2 | P: /devices/pci0000:00/0000:00:01.1/0000:02:00.0/nvme/nvme0/nvme0n1 3 | N: nvme0n1 4 | S: disk/by-id/lvm-pv-uuid-Py4eod-qfzj-i8Q3-Dxu6-xf0Q-H3Wr-w5Fo8V 5 | E: DEVLINKS=/dev/disk/by-id/lvm-pv-uuid-Py4eod-qfzj-i8Q3-Dxu6-xf0Q-H3Wr-w5Fo8V 6 | E: DEVNAME=/dev/nvme0n1 7 | E: DEVPATH=/devices/pci0000:00/0000:00:01.1/0000:02:00.0/nvme/nvme0/nvme0n1 8 | E: DEVTYPE=disk 9 | E: ID_FS_TYPE=LVM2_member 10 | E: ID_FS_USAGE=raid 11 | E: ID_FS_UUID=Py4eod-qfzj-i8Q3-Dxu6-xf0Q-H3Wr-w5Fo8V 12 | E: ID_FS_UUID_ENC=Py4eod-qfzj-i8Q3-Dxu6-xf0Q-H3Wr-w5Fo8V 13 | E: ID_FS_VERSION=LVM2 001 14 | E: MAJOR=259 15 | E: MINOR=0 16 | E: SUBSYSTEM=block 17 | E: TAGS=:systemd: 18 | E: USEC_INITIALIZED=3842 19 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/disklist: -------------------------------------------------------------------------------- 1 | sda 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/disklist_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "sda" : { 3 | "gpt" : 1, 4 | "devpath" : "/dev/sda", 5 | "type" : "unknown", 6 | "model" : "MODEL1", 7 | "health" : "UNKNOWN", 8 | "osdid" : -1, 9 | "osdid-list" : null, 10 | "wwn" : "0x0000000000000000", 11 | "vendor" : "VENDOR1", 12 | "rpm" : -1, 13 | "size" : 5120000, 14 | "serial" : "SER2", 15 | "wearout" : "N/A", 16 | "by_id_link" : "/dev/disk/by-id/scsi-00000000000000000" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/sda/device/model: -------------------------------------------------------------------------------- 1 | MODEL1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/sda/device/vendor: -------------------------------------------------------------------------------- 1 | VENDOR1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/sda/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/sda/size: -------------------------------------------------------------------------------- 1 | 10000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/sda_smart: -------------------------------------------------------------------------------- 1 | === START OF READ SMART DATA SECTION === 2 | SMART Health Status: OK 3 | 4 | Percentage used endurance indicator: 0% 5 | Current Drive Temperature: 20 C 6 | Drive Trip Temperature: 70 C 7 | 8 | Manufactured in week 47 of year 2012 9 | Specified cycle count over device lifetime: 0 10 | Accumulated start-stop cycles: 0 11 | Specified load-unload count over device lifetime: 0 12 | Accumulated load-unload cycles: 0 13 | Elements in grown defect list: 0 14 | 15 | Vendor (Seagate) cache information 16 | Blocks sent to initiator = 1286675833552896 17 | 18 | Vendor (Seagate/Hitachi) factory information 19 | number of hours powered up = 7127.12 20 | number of minutes until next internal SMART test = 0 21 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/sda_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "health" : "OK", 3 | "text" : "\nPercentage used endurance indicator: 0%\nCurrent Drive Temperature: 20 C\nDrive Trip Temperature: 70 C\n\nManufactured in week 47 of year 2012\nSpecified cycle count over device lifetime: 0\nAccumulated start-stop cycles: 0\nSpecified load-unload count over device lifetime: 0\nAccumulated load-unload cycles: 0\nElements in grown defect list: 0\n\nVendor (Seagate) cache information\n Blocks sent to initiator = 1286675833552896\n\nVendor (Seagate/Hitachi) factory information\n number of hours powered up = 7127.12\n number of minutes until next internal SMART test = 0\n", 4 | "type" : "text", 5 | "wearout": 100 6 | } 7 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas/sda_udevadm: -------------------------------------------------------------------------------- 1 | P: /devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sda 2 | N: sda 3 | S: disk/by-id/scsi-00000000000000000 4 | S: disk/by-id/wwn-0x0000000000000000 5 | S: disk/by-path/pci-0000:02:00.0-sas-0x0000000000000000-lun-0 6 | E: DEVLINKS=/dev/disk/by-id/scsi-00000000000000000 /dev/disk/by-id/wwn-0x0000000000000000 /dev/disk/by-path/pci-0000:02:00.0-sas-0x0000000000000000-lun-0 7 | E: DEVNAME=/dev/sda 8 | E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sda 9 | E: DEVTYPE=disk 10 | E: ID_BUS=scsi 11 | E: ID_MODEL=MODEL1 12 | E: ID_MODEL_ENC=MODEL1\x20\x20\x20\x20\x20\x20 13 | E: ID_PART_TABLE_TYPE=gpt 14 | E: ID_PART_TABLE_UUID=605740f0-44a1-4dc5-9fea-bde166df963e 15 | E: ID_PATH=pci-0000:02:00.0-sas-0x0000000000000000-lun-0 16 | E: ID_PATH_TAG=pci-0000_02_00_0-sas-0x0000000000000000-lun-0 17 | E: ID_REVISION=ES64 18 | E: ID_SCSI=1 19 | E: ID_SCSI_SERIAL=SERIAL 20 | E: ID_SERIAL=SERIAL2 21 | E: ID_SERIAL_SHORT=SER2 22 | E: ID_TYPE=disk 23 | E: ID_VENDOR=VENDOR1 24 | E: ID_VENDOR_ENC=VENDOR1\x20 25 | E: ID_WWN=0x0000000000000000 26 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 27 | E: MAJOR=8 28 | E: MINOR=0 29 | E: SUBSYSTEM=block 30 | E: TAGS=:systemd: 31 | E: USEC_INITIALIZED=667541 32 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/disklist: -------------------------------------------------------------------------------- 1 | sda 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/disklist_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "sda" : { 3 | "gpt" : 1, 4 | "devpath" : "/dev/sda", 5 | "type" : "ssd", 6 | "model" : "MODEL1", 7 | "health" : "OK", 8 | "osdid" : -1, 9 | "osdid-list" : null, 10 | "wwn" : "0x0000000000000000", 11 | "vendor" : "VENDOR1", 12 | "rpm" : 0, 13 | "size" : 5120000, 14 | "serial" : "SER2", 15 | "wearout" : 100, 16 | "by_id_link" : "/dev/disk/by-id/scsi-00000000000000000" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/sda/device/model: -------------------------------------------------------------------------------- 1 | MODEL1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/sda/device/vendor: -------------------------------------------------------------------------------- 1 | VENDOR1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/sda/queue/rotational: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/sda/size: -------------------------------------------------------------------------------- 1 | 10000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/sda_smart: -------------------------------------------------------------------------------- 1 | === START OF READ SMART DATA SECTION === 2 | SMART Health Status: OK 3 | 4 | Percentage used endurance indicator: 0% 5 | Current Drive Temperature: 20 C 6 | Drive Trip Temperature: 70 C 7 | 8 | Manufactured in week 47 of year 2012 9 | Specified cycle count over device lifetime: 0 10 | Accumulated start-stop cycles: 0 11 | Specified load-unload count over device lifetime: 0 12 | Accumulated load-unload cycles: 0 13 | Elements in grown defect list: 0 14 | 15 | Vendor (Seagate) cache information 16 | Blocks sent to initiator = 1286675833552896 17 | 18 | Vendor (Seagate/Hitachi) factory information 19 | number of hours powered up = 7127.12 20 | number of minutes until next internal SMART test = 0 21 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/sda_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "health" : "OK", 3 | "text" : "\nPercentage used endurance indicator: 0%\nCurrent Drive Temperature: 20 C\nDrive Trip Temperature: 70 C\n\nManufactured in week 47 of year 2012\nSpecified cycle count over device lifetime: 0\nAccumulated start-stop cycles: 0\nSpecified load-unload count over device lifetime: 0\nAccumulated load-unload cycles: 0\nElements in grown defect list: 0\n\nVendor (Seagate) cache information\n Blocks sent to initiator = 1286675833552896\n\nVendor (Seagate/Hitachi) factory information\n number of hours powered up = 7127.12\n number of minutes until next internal SMART test = 0\n", 4 | "type" : "text", 5 | "wearout": 100 6 | } 7 | -------------------------------------------------------------------------------- /src/test/disk_tests/sas_ssd/sda_udevadm: -------------------------------------------------------------------------------- 1 | P: /devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sda 2 | N: sda 3 | S: disk/by-id/scsi-00000000000000000 4 | S: disk/by-id/wwn-0x0000000000000000 5 | S: disk/by-path/pci-0000:02:00.0-sas-0x0000000000000000-lun-0 6 | E: DEVLINKS=/dev/disk/by-id/scsi-00000000000000000 /dev/disk/by-id/wwn-0x0000000000000000 /dev/disk/by-path/pci-0000:02:00.0-sas-0x0000000000000000-lun-0 7 | E: DEVNAME=/dev/sda 8 | E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sda 9 | E: DEVTYPE=disk 10 | E: ID_BUS=scsi 11 | E: ID_MODEL=MODEL1 12 | E: ID_MODEL_ENC=MODEL1\x20\x20\x20\x20\x20\x20 13 | E: ID_PART_TABLE_TYPE=gpt 14 | E: ID_PART_TABLE_UUID=605740f0-44a1-4dc5-9fea-bde166df963e 15 | E: ID_PATH=pci-0000:02:00.0-sas-0x0000000000000000-lun-0 16 | E: ID_PATH_TAG=pci-0000_02_00_0-sas-0x0000000000000000-lun-0 17 | E: ID_REVISION=ES64 18 | E: ID_SCSI=1 19 | E: ID_SCSI_SERIAL=SERIAL 20 | E: ID_SERIAL=SERIAL2 21 | E: ID_SERIAL_SHORT=SER2 22 | E: ID_TYPE=disk 23 | E: ID_VENDOR=VENDOR1 24 | E: ID_VENDOR_ENC=VENDOR1\x20 25 | E: ID_WWN=0x0000000000000000 26 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 27 | E: MAJOR=8 28 | E: MINOR=0 29 | E: SUBSYSTEM=block 30 | E: TAGS=:systemd: 31 | E: USEC_INITIALIZED=667541 32 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/disklist: -------------------------------------------------------------------------------- 1 | sda 2 | sdb 3 | sdc 4 | sdd 5 | sde 6 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/disklist_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "sda" : { 3 | "serial" : "000000000000", 4 | "vendor" : "ATA", 5 | "rpm" : 0, 6 | "gpt" : 1, 7 | "health" : "PASSED", 8 | "wearout" : "100", 9 | "osdid" : -1, 10 | "osdid-list" : null, 11 | "size" : 512000, 12 | "type" : "ssd", 13 | "devpath" : "/dev/sda", 14 | "model" : "Crucial_CT500MX200SSD1", 15 | "wwn" : "0x0000000000000000" 16 | }, 17 | "sdb" : { 18 | "model" : "INTEL_SSDSC2BB080G6", 19 | "devpath" : "/dev/sdb", 20 | "osdid" : -1, 21 | "osdid-list" : null, 22 | "type" : "ssd", 23 | "size" : 512000, 24 | "wwn" : "0x0000000000000000", 25 | "gpt" : 1, 26 | "rpm" : 0, 27 | "vendor" : "ATA", 28 | "serial" : "000000000000000000", 29 | "wearout" : "97", 30 | "health" : "PASSED" 31 | }, 32 | "sdc" : { 33 | "wwn" : "0x0000000000000000", 34 | "devpath" : "/dev/sdc", 35 | "model" : "Samsung SSD 850 PRO 512GB", 36 | "osdid" : -1, 37 | "osdid-list" : null, 38 | "type" : "ssd", 39 | "size" : 512000, 40 | "wearout" : "99", 41 | "health" : "PASSED", 42 | "gpt" : 1, 43 | "rpm" : 0, 44 | "vendor" : "ATA", 45 | "serial" : "000000000000" 46 | }, 47 | "sdd" : { 48 | "rpm" : 0, 49 | "gpt" : 1, 50 | "serial" : "000000000000", 51 | "vendor" : "ATA", 52 | "wearout" : "100", 53 | "health" : "PASSED", 54 | "devpath" : "/dev/sdd", 55 | "model" : "SanDisk SD8SB8U1T001122", 56 | "size" : 512000, 57 | "osdid" : -1, 58 | "osdid-list" : null, 59 | "type" : "ssd", 60 | "wwn" : "0x0000000000000000" 61 | }, 62 | "sde" : { 63 | "type" : "ssd", 64 | "osdid" : -1, 65 | "osdid-list" : null, 66 | "size" : 512000, 67 | "model" : "KINGSTON SHFS37A120G", 68 | "devpath" : "/dev/sde", 69 | "wwn" : "0x0000000000000000", 70 | "vendor" : "ATA", 71 | "serial" : "000000000000", 72 | "gpt" : 1, 73 | "rpm" : 0, 74 | "health" : "PASSED", 75 | "wearout" : "91" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sda/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sda/queue/rotational: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sda/size: -------------------------------------------------------------------------------- 1 | 1000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sda_smart: -------------------------------------------------------------------------------- 1 | smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.4.21-1-pve] (local build) 2 | Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | 7 | SMART Attributes Data Structure revision number: 16 8 | Vendor Specific SMART Attributes with Thresholds: 9 | ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 10 | 1 Raw_Read_Error_Rate POSR-K 100 100 000 - 0 11 | 5 Reallocate_NAND_Blk_Cnt -O--CK 100 100 010 - 0 12 | 9 Power_On_Hours -O--CK 100 100 000 - 1309 13 | 12 Power_Cycle_Count -O--CK 100 100 000 - 200 14 | 171 Program_Fail_Count -O--CK 100 100 000 - 0 15 | 172 Erase_Fail_Count -O--CK 100 100 000 - 0 16 | 173 Ave_Block-Erase_Count -O--CK 100 100 000 - 12 17 | 174 Unexpect_Power_Loss_Ct -O--CK 100 100 000 - 53 18 | 180 Unused_Reserve_NAND_Blk PO--CK 000 000 000 - 5565 19 | 183 SATA_Interfac_Downshift -O--CK 100 100 000 - 0 20 | 184 Error_Correction_Count -O--CK 100 100 000 - 0 21 | 187 Reported_Uncorrect -O--CK 100 100 000 - 0 22 | 194 Temperature_Celsius -O---K 068 054 000 - 32 (Min/Max 22/46) 23 | 196 Reallocated_Event_Count -O--CK 100 100 000 - 0 24 | 197 Current_Pending_Sector -O--CK 100 100 000 - 0 25 | 198 Offline_Uncorrectable ----CK 100 100 000 - 0 26 | 199 UDMA_CRC_Error_Count -O--CK 100 100 000 - 0 27 | 202 Percent_Lifetime_Used ----CK 100 100 001 - 0 28 | 206 Write_Error_Rate -OSR-- 100 100 000 - 0 29 | 210 Success_RAIN_Recov_Cnt -O--CK 100 100 000 - 0 30 | 246 Total_Host_Sector_Write -O--CK 100 100 000 - 6751830403 31 | 247 Host_Program_Page_Count -O--CK 100 100 000 - 211228065 32 | 248 Bckgnd_Program_Page_Cnt -O--CK 100 100 000 - 253276904 33 | ||||||_ K auto-keep 34 | |||||__ C event count 35 | ||||___ R error rate 36 | |||____ S speed/performance 37 | ||_____ O updated online 38 | |______ P prefailure warning 39 | 40 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sda_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "ata", 3 | "health" : "PASSED", 4 | "attributes" : [ 5 | { 6 | "worst" : 100, 7 | "threshold" : 0, 8 | "name" : "Raw_Read_Error_Rate", 9 | "id" : " 1", 10 | "flags" : "POSR-K", 11 | "raw" : "0", 12 | "fail" : "-", 13 | "normalized" : 100, 14 | "value" : 100 15 | }, 16 | { 17 | "worst" : 100, 18 | "threshold" : 10, 19 | "name" : "Reallocate_NAND_Blk_Cnt", 20 | "id" : " 5", 21 | "flags" : "-O--CK", 22 | "raw" : "0", 23 | "fail" : "-", 24 | "normalized" : 100, 25 | "value" : 100 26 | }, 27 | { 28 | "name" : "Power_On_Hours", 29 | "threshold" : 0, 30 | "worst" : 100, 31 | "normalized" : 100, 32 | "value" : 100, 33 | "fail" : "-", 34 | "raw" : "1309", 35 | "flags" : "-O--CK", 36 | "id" : " 9" 37 | }, 38 | { 39 | "threshold" : 0, 40 | "name" : "Power_Cycle_Count", 41 | "worst" : 100, 42 | "raw" : "200", 43 | "flags" : "-O--CK", 44 | "fail" : "-", 45 | "id" : " 12", 46 | "normalized" : 100, 47 | "value" : 100 48 | }, 49 | { 50 | "normalized" : 100, 51 | "value" : 100, 52 | "raw" : "0", 53 | "flags" : "-O--CK", 54 | "fail" : "-", 55 | "id" : "171", 56 | "threshold" : 0, 57 | "name" : "Program_Fail_Count", 58 | "worst" : 100 59 | }, 60 | { 61 | "normalized" : 100, 62 | "value" : 100, 63 | "id" : "172", 64 | "raw" : "0", 65 | "flags" : "-O--CK", 66 | "fail" : "-", 67 | "worst" : 100, 68 | "threshold" : 0, 69 | "name" : "Erase_Fail_Count" 70 | }, 71 | { 72 | "threshold" : 0, 73 | "name" : "Ave_Block-Erase_Count", 74 | "worst" : 100, 75 | "raw" : "12", 76 | "flags" : "-O--CK", 77 | "fail" : "-", 78 | "id" : "173", 79 | "normalized" : 100, 80 | "value" : 100 81 | }, 82 | { 83 | "name" : "Unexpect_Power_Loss_Ct", 84 | "threshold" : 0, 85 | "worst" : 100, 86 | "normalized" : 100, 87 | "value" : 100, 88 | "fail" : "-", 89 | "raw" : "53", 90 | "flags" : "-O--CK", 91 | "id" : "174" 92 | }, 93 | { 94 | "raw" : "5565", 95 | "flags" : "PO--CK", 96 | "fail" : "-", 97 | "id" : "180", 98 | "normalized" : 0, 99 | "value" : 0, 100 | "threshold" : 0, 101 | "name" : "Unused_Reserve_NAND_Blk", 102 | "worst" : 0 103 | }, 104 | { 105 | "name" : "SATA_Interfac_Downshift", 106 | "threshold" : 0, 107 | "worst" : 100, 108 | "fail" : "-", 109 | "raw" : "0", 110 | "flags" : "-O--CK", 111 | "id" : "183", 112 | "normalized" : 100, 113 | "value" : 100 114 | }, 115 | { 116 | "worst" : 100, 117 | "threshold" : 0, 118 | "name" : "Error_Correction_Count", 119 | "id" : "184", 120 | "flags" : "-O--CK", 121 | "raw" : "0", 122 | "fail" : "-", 123 | "normalized" : 100, 124 | "value" : 100 125 | }, 126 | { 127 | "name" : "Reported_Uncorrect", 128 | "threshold" : 0, 129 | "worst" : 100, 130 | "normalized" : 100, 131 | "value" : 100, 132 | "fail" : "-", 133 | "flags" : "-O--CK", 134 | "raw" : "0", 135 | "id" : "187" 136 | }, 137 | { 138 | "worst" : 54, 139 | "name" : "Temperature_Celsius", 140 | "threshold" : 0, 141 | "id" : "194", 142 | "fail" : "-", 143 | "flags" : "-O---K", 144 | "raw" : "32 (Min/Max 22/46)", 145 | "normalized" : 68, 146 | "value" : 68 147 | }, 148 | { 149 | "raw" : "0", 150 | "flags" : "-O--CK", 151 | "fail" : "-", 152 | "id" : "196", 153 | "normalized" : 100, 154 | "value" : 100, 155 | "threshold" : 0, 156 | "name" : "Reallocated_Event_Count", 157 | "worst" : 100 158 | }, 159 | { 160 | "id" : "197", 161 | "fail" : "-", 162 | "flags" : "-O--CK", 163 | "raw" : "0", 164 | "normalized" : 100, 165 | "value" : 100, 166 | "worst" : 100, 167 | "name" : "Current_Pending_Sector", 168 | "threshold" : 0 169 | }, 170 | { 171 | "threshold" : 0, 172 | "name" : "Offline_Uncorrectable", 173 | "worst" : 100, 174 | "flags" : "----CK", 175 | "raw" : "0", 176 | "fail" : "-", 177 | "id" : "198", 178 | "normalized" : 100, 179 | "value" : 100 180 | }, 181 | { 182 | "worst" : 100, 183 | "name" : "UDMA_CRC_Error_Count", 184 | "threshold" : 0, 185 | "id" : "199", 186 | "fail" : "-", 187 | "raw" : "0", 188 | "flags" : "-O--CK", 189 | "normalized" : 100, 190 | "value" : 100 191 | }, 192 | { 193 | "normalized" : 100, 194 | "value" : 100, 195 | "id" : "202", 196 | "flags" : "----CK", 197 | "raw" : "0", 198 | "fail" : "-", 199 | "worst" : 100, 200 | "threshold" : 1, 201 | "name" : "Percent_Lifetime_Used" 202 | }, 203 | { 204 | "name" : "Write_Error_Rate", 205 | "threshold" : 0, 206 | "worst" : 100, 207 | "normalized" : 100, 208 | "value" : 100, 209 | "fail" : "-", 210 | "raw" : "0", 211 | "flags" : "-OSR--", 212 | "id" : "206" 213 | }, 214 | { 215 | "name" : "Success_RAIN_Recov_Cnt", 216 | "threshold" : 0, 217 | "worst" : 100, 218 | "fail" : "-", 219 | "raw" : "0", 220 | "flags" : "-O--CK", 221 | "id" : "210", 222 | "normalized" : 100, 223 | "value" : 100 224 | }, 225 | { 226 | "normalized" : 100, 227 | "value" : 100, 228 | "id" : "246", 229 | "fail" : "-", 230 | "raw" : "6751830403", 231 | "flags" : "-O--CK", 232 | "worst" : 100, 233 | "name" : "Total_Host_Sector_Write", 234 | "threshold" : 0 235 | }, 236 | { 237 | "name" : "Host_Program_Page_Count", 238 | "threshold" : 0, 239 | "worst" : 100, 240 | "fail" : "-", 241 | "raw" : "211228065", 242 | "flags" : "-O--CK", 243 | "id" : "247", 244 | "normalized" : 100, 245 | "value" : 100 246 | }, 247 | { 248 | "normalized" : 100, 249 | "value" : 100, 250 | "fail" : "-", 251 | "raw" : "253276904", 252 | "flags" : "-O--CK", 253 | "id" : "248", 254 | "name" : "Bckgnd_Program_Page_Cnt", 255 | "threshold" : 0, 256 | "worst" : 100 257 | } 258 | ] 259 | } 260 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sda_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sda 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=Crucial_CT500MX200SSD1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_SERIAL=Crucial_CT500MX200SSD1_000000000000 8 | E: ID_SERIAL_SHORT=000000000000 9 | E: ID_TYPE=disk 10 | E: ID_WWN=0x0000000000000000 11 | 12 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdb/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdb/queue/rotational: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdb/size: -------------------------------------------------------------------------------- 1 | 1000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdb_smart: -------------------------------------------------------------------------------- 1 | smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.4.21-1-pve] (local build) 2 | Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | 7 | SMART Attributes Data Structure revision number: 1 8 | Vendor Specific SMART Attributes with Thresholds: 9 | ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 10 | 5 Reallocated_Sector_Ct -O--CK 100 100 000 - 0 11 | 9 Power_On_Hours -O--CK 100 100 000 - 1259 12 | 12 Power_Cycle_Count -O--CK 100 100 000 - 191 13 | 170 Available_Reservd_Space PO--CK 100 100 010 - 0 14 | 171 Program_Fail_Count -O--CK 100 100 000 - 0 15 | 172 Erase_Fail_Count -O--CK 100 100 000 - 0 16 | 174 Unsafe_Shutdown_Count -O--CK 100 100 000 - 164 17 | 175 Power_Loss_Cap_Test PO--CK 100 100 010 - 5670 (1 343) 18 | 183 SATA_Downshift_Count -O--CK 100 100 000 - 0 19 | 184 End-to-End_Error PO--CK 100 100 090 - 0 20 | 187 Reported_Uncorrect -O--CK 100 100 000 - 0 21 | 190 Temperature_Case -O---K 072 071 000 - 28 (Min/Max 21/30) 22 | 192 Unsafe_Shutdown_Count -O--CK 100 100 000 - 164 23 | 194 Temperature_Internal -O---K 100 100 000 - 28 24 | 197 Current_Pending_Sector -O--C- 100 100 000 - 0 25 | 199 CRC_Error_Count -OSRCK 100 100 000 - 0 26 | 225 Host_Writes_32MiB -O--CK 100 100 000 - 296600 27 | 226 Workld_Media_Wear_Indic -O--CK 100 100 000 - 3747 28 | 227 Workld_Host_Reads_Perc -O--CK 100 100 000 - 0 29 | 228 Workload_Minutes -O--CK 100 100 000 - 75111 30 | 232 Available_Reservd_Space PO--CK 100 100 010 - 0 31 | 233 Media_Wearout_Indicator -O--CK 097 097 000 - 0 32 | 234 Thermal_Throttle -O--CK 100 100 000 - 0/0 33 | 241 Host_Writes_32MiB -O--CK 100 100 000 - 296600 34 | 242 Host_Reads_32MiB -O--CK 100 100 000 - 1207 35 | ||||||_ K auto-keep 36 | |||||__ C event count 37 | ||||___ R error rate 38 | |||____ S speed/performance 39 | ||_____ O updated online 40 | |______ P prefailure warning 41 | 42 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdb_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "health" : "PASSED", 3 | "type" : "ata", 4 | "attributes" : [ 5 | { 6 | "name" : "Reallocated_Sector_Ct", 7 | "threshold" : 0, 8 | "worst" : 100, 9 | "fail" : "-", 10 | "flags" : "-O--CK", 11 | "raw" : "0", 12 | "id" : " 5", 13 | "normalized" : 100, 14 | "value" : 100 15 | }, 16 | { 17 | "worst" : 100, 18 | "name" : "Power_On_Hours", 19 | "threshold" : 0, 20 | "id" : " 9", 21 | "fail" : "-", 22 | "raw" : "1259", 23 | "flags" : "-O--CK", 24 | "normalized" : 100, 25 | "value" : 100 26 | }, 27 | { 28 | "normalized" : 100, 29 | "value" : 100, 30 | "id" : " 12", 31 | "raw" : "191", 32 | "flags" : "-O--CK", 33 | "fail" : "-", 34 | "worst" : 100, 35 | "threshold" : 0, 36 | "name" : "Power_Cycle_Count" 37 | }, 38 | { 39 | "id" : "170", 40 | "raw" : "0", 41 | "flags" : "PO--CK", 42 | "fail" : "-", 43 | "normalized" : 100, 44 | "value" : 100, 45 | "worst" : 100, 46 | "threshold" : 10, 47 | "name" : "Available_Reservd_Space" 48 | }, 49 | { 50 | "threshold" : 0, 51 | "name" : "Program_Fail_Count", 52 | "worst" : 100, 53 | "normalized" : 100, 54 | "value" : 100, 55 | "raw" : "0", 56 | "flags" : "-O--CK", 57 | "fail" : "-", 58 | "id" : "171" 59 | }, 60 | { 61 | "worst" : 100, 62 | "name" : "Erase_Fail_Count", 63 | "threshold" : 0, 64 | "id" : "172", 65 | "fail" : "-", 66 | "flags" : "-O--CK", 67 | "raw" : "0", 68 | "normalized" : 100, 69 | "value" : 100 70 | }, 71 | { 72 | "worst" : 100, 73 | "threshold" : 0, 74 | "name" : "Unsafe_Shutdown_Count", 75 | "normalized" : 100, 76 | "value" : 100, 77 | "id" : "174", 78 | "raw" : "164", 79 | "flags" : "-O--CK", 80 | "fail" : "-" 81 | }, 82 | { 83 | "normalized" : 100, 84 | "value" : 100, 85 | "fail" : "-", 86 | "flags" : "PO--CK", 87 | "raw" : "5670 (1 343)", 88 | "id" : "175", 89 | "name" : "Power_Loss_Cap_Test", 90 | "threshold" : 10, 91 | "worst" : 100 92 | }, 93 | { 94 | "worst" : 100, 95 | "threshold" : 0, 96 | "name" : "SATA_Downshift_Count", 97 | "normalized" : 100, 98 | "value" : 100, 99 | "id" : "183", 100 | "raw" : "0", 101 | "flags" : "-O--CK", 102 | "fail" : "-" 103 | }, 104 | { 105 | "worst" : 100, 106 | "name" : "End-to-End_Error", 107 | "threshold" : 90, 108 | "normalized" : 100, 109 | "value" : 100, 110 | "id" : "184", 111 | "fail" : "-", 112 | "raw" : "0", 113 | "flags" : "PO--CK" 114 | }, 115 | { 116 | "worst" : 100, 117 | "name" : "Reported_Uncorrect", 118 | "threshold" : 0, 119 | "normalized" : 100, 120 | "value" : 100, 121 | "id" : "187", 122 | "fail" : "-", 123 | "raw" : "0", 124 | "flags" : "-O--CK" 125 | }, 126 | { 127 | "worst" : 71, 128 | "name" : "Temperature_Case", 129 | "threshold" : 0, 130 | "id" : "190", 131 | "fail" : "-", 132 | "raw" : "28 (Min/Max 21/30)", 133 | "flags" : "-O---K", 134 | "normalized" : 72, 135 | "value" : 72 136 | }, 137 | { 138 | "worst" : 100, 139 | "threshold" : 0, 140 | "name" : "Unsafe_Shutdown_Count", 141 | "normalized" : 100, 142 | "value" : 100, 143 | "id" : "192", 144 | "raw" : "164", 145 | "flags" : "-O--CK", 146 | "fail" : "-" 147 | }, 148 | { 149 | "id" : "194", 150 | "fail" : "-", 151 | "raw" : "28", 152 | "flags" : "-O---K", 153 | "normalized" : 100, 154 | "value" : 100, 155 | "worst" : 100, 156 | "name" : "Temperature_Internal", 157 | "threshold" : 0 158 | }, 159 | { 160 | "worst" : 100, 161 | "threshold" : 0, 162 | "name" : "Current_Pending_Sector", 163 | "normalized" : 100, 164 | "value" : 100, 165 | "id" : "197", 166 | "raw" : "0", 167 | "flags" : "-O--C-", 168 | "fail" : "-" 169 | }, 170 | { 171 | "worst" : 100, 172 | "threshold" : 0, 173 | "name" : "CRC_Error_Count", 174 | "normalized" : 100, 175 | "value" : 100, 176 | "id" : "199", 177 | "flags" : "-OSRCK", 178 | "raw" : "0", 179 | "fail" : "-" 180 | }, 181 | { 182 | "worst" : 100, 183 | "name" : "Host_Writes_32MiB", 184 | "threshold" : 0, 185 | "id" : "225", 186 | "fail" : "-", 187 | "flags" : "-O--CK", 188 | "raw" : "296600", 189 | "normalized" : 100, 190 | "value" : 100 191 | }, 192 | { 193 | "normalized" : 100, 194 | "value" : 100, 195 | "id" : "226", 196 | "raw" : "3747", 197 | "flags" : "-O--CK", 198 | "fail" : "-", 199 | "worst" : 100, 200 | "threshold" : 0, 201 | "name" : "Workld_Media_Wear_Indic" 202 | }, 203 | { 204 | "worst" : 100, 205 | "threshold" : 0, 206 | "name" : "Workld_Host_Reads_Perc", 207 | "id" : "227", 208 | "raw" : "0", 209 | "flags" : "-O--CK", 210 | "fail" : "-", 211 | "normalized" : 100, 212 | "value" : 100 213 | }, 214 | { 215 | "threshold" : 0, 216 | "name" : "Workload_Minutes", 217 | "worst" : 100, 218 | "raw" : "75111", 219 | "flags" : "-O--CK", 220 | "fail" : "-", 221 | "id" : "228", 222 | "normalized" : 100, 223 | "value" : 100 224 | }, 225 | { 226 | "fail" : "-", 227 | "raw" : "0", 228 | "flags" : "PO--CK", 229 | "id" : "232", 230 | "normalized" : 100, 231 | "value" : 100, 232 | "name" : "Available_Reservd_Space", 233 | "threshold" : 10, 234 | "worst" : 100 235 | }, 236 | { 237 | "normalized" : 97, 238 | "value" : 97, 239 | "fail" : "-", 240 | "flags" : "-O--CK", 241 | "raw" : "0", 242 | "id" : "233", 243 | "name" : "Media_Wearout_Indicator", 244 | "threshold" : 0, 245 | "worst" : 97 246 | }, 247 | { 248 | "normalized" : 100, 249 | "value" : 100, 250 | "fail" : "-", 251 | "raw" : "0/0", 252 | "flags" : "-O--CK", 253 | "id" : "234", 254 | "name" : "Thermal_Throttle", 255 | "threshold" : 0, 256 | "worst" : 100 257 | }, 258 | { 259 | "worst" : 100, 260 | "name" : "Host_Writes_32MiB", 261 | "threshold" : 0, 262 | "id" : "241", 263 | "fail" : "-", 264 | "raw" : "296600", 265 | "flags" : "-O--CK", 266 | "normalized" : 100, 267 | "value" : 100 268 | }, 269 | { 270 | "normalized" : 100, 271 | "value" : 100, 272 | "id" : "242", 273 | "fail" : "-", 274 | "raw" : "1207", 275 | "flags" : "-O--CK", 276 | "worst" : 100, 277 | "name" : "Host_Reads_32MiB", 278 | "threshold" : 0 279 | } 280 | ] 281 | } 282 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdb_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdb 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=INTEL_SSDSC2BB080G6 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=21c18951-8568-488c-a2a8-24441eb4b165 8 | E: ID_SERIAL=INTEL_SSDSC2BB080G6_000000000000000000 9 | E: ID_SERIAL_SHORT=000000000000000000 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdc/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdc/queue/rotational: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdc/size: -------------------------------------------------------------------------------- 1 | 1000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdc_smart: -------------------------------------------------------------------------------- 1 | smartctl 6.4 2014-10-07 r4002 [x86_64-linux-4.4.8-1-pve] (local build) 2 | Copyright (C) 2002-14, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | 7 | SMART Attributes Data Structure revision number: 1 8 | Vendor Specific SMART Attributes with Thresholds: 9 | ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 10 | 177 Wear_Leveling_Count PO--CK 099 099 000 - 34 11 | ||||||_ K auto-keep 12 | |||||__ C event count 13 | ||||___ R error rate 14 | |||____ S speed/performance 15 | ||_____ O updated onlinie 16 | |______ P prefailure warning 17 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdc_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "ata", 3 | "health" : "PASSED", 4 | "attributes" : [ 5 | { 6 | "normalized" : 99, 7 | "value" : 99, 8 | "fail" : "-", 9 | "raw" : "34", 10 | "flags" : "PO--CK", 11 | "id" : "177", 12 | "name" : "Wear_Leveling_Count", 13 | "threshold" : 0, 14 | "worst" : 99 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdc_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdc 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=Samsung SSD 850 PRO 512GB 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_SERIAL=SAMSUNG_00000000 8 | E: ID_SERIAL_SHORT=000000000000 9 | E: ID_TYPE=disk 10 | E: ID_WWN=0x0000000000000000 11 | 12 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdd/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdd/queue/rotational: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdd/size: -------------------------------------------------------------------------------- 1 | 1000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdd_smart: -------------------------------------------------------------------------------- 1 | smartctl 6.4 2014-10-07 r4002 [x86_64-linux-4.4.19-1-pve] (local build) 2 | Copyright (C) 2002-14, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | 7 | SMART Attributes Data Structure revision number: 4 8 | Vendor Specific SMART Attributes with Thresholds: 9 | ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 10 | 5 Reallocated_Sector_Ct -O--CK 100 100 --- - 0 11 | 9 Power_On_Hours -O--CK 100 100 --- - 799 12 | 12 Power_Cycle_Count -O--CK 100 100 --- - 92 13 | 165 Unknown_Attribute -O--CK 100 100 --- - 9699447 14 | 166 Unknown_Attribute -O--CK 100 100 --- - 1 15 | 167 Unknown_Attribute -O--CK 100 100 --- - 46 16 | 168 Unknown_Attribute -O--CK 100 100 --- - 5 17 | 169 Unknown_Attribute -O--CK 100 100 --- - 1079 18 | 170 Unknown_Attribute -O--CK 100 100 --- - 0 19 | 171 Unknown_Attribute -O--CK 100 100 --- - 0 20 | 172 Unknown_Attribute -O--CK 100 100 --- - 0 21 | 173 Unknown_Attribute -O--CK 100 100 --- - 1 22 | 174 Unknown_Attribute -O--CK 100 100 --- - 22 23 | 184 End-to-End_Error -O--CK 100 100 --- - 0 24 | 187 Reported_Uncorrect -O--CK 100 100 --- - 0 25 | 188 Command_Timeout -O--CK 100 100 --- - 0 26 | 194 Temperature_Celsius -O---K 073 064 --- - 27 (Min/Max 23/64) 27 | 199 UDMA_CRC_Error_Count -O--CK 100 100 --- - 0 28 | 230 Unknown_SSD_Attribute -O--CK 100 100 --- - 146029805602 29 | 232 Available_Reservd_Space PO--CK 100 100 004 - 100 30 | 233 Media_Wearout_Indicator -O--CK 100 100 --- - 1574 31 | 234 Unknown_Attribute -O--CK 100 100 --- - 2303 32 | 241 Total_LBAs_Written ----CK 253 253 --- - 2111 33 | 242 Total_LBAs_Read ----CK 253 253 --- - 1542 34 | 244 Unknown_Attribute -O--CK 000 100 --- - 0 35 | ||||||_ K auto-keep 36 | |||||__ C event count 37 | ||||___ R error rate 38 | |||____ S speed/performance 39 | ||_____ O updated online 40 | |______ P prefailure warning 41 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdd_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes" : [ 3 | { 4 | "id" : " 5", 5 | "flags" : "-O--CK", 6 | "fail" : "-", 7 | "worst" : 100, 8 | "raw" : "0", 9 | "threshold" : 0, 10 | "normalized" : 100, 11 | "value" : 100, 12 | "name" : "Reallocated_Sector_Ct" 13 | }, 14 | { 15 | "worst" : 100, 16 | "fail" : "-", 17 | "flags" : "-O--CK", 18 | "id" : " 9", 19 | "name" : "Power_On_Hours", 20 | "threshold" : 0, 21 | "normalized" : 100, 22 | "value" : 100, 23 | "raw" : "799" 24 | }, 25 | { 26 | "name" : "Power_Cycle_Count", 27 | "raw" : "92", 28 | "threshold" : 0, 29 | "normalized" : 100, 30 | "value" : 100, 31 | "fail" : "-", 32 | "worst" : 100, 33 | "id" : " 12", 34 | "flags" : "-O--CK" 35 | }, 36 | { 37 | "worst" : 100, 38 | "fail" : "-", 39 | "flags" : "-O--CK", 40 | "id" : "165", 41 | "name" : "Unknown_Attribute", 42 | "threshold" : 0, 43 | "normalized" : 100, 44 | "value" : 100, 45 | "raw" : "9699447" 46 | }, 47 | { 48 | "normalized" : 100, 49 | "value" : 100, 50 | "threshold" : 0, 51 | "raw" : "1", 52 | "name" : "Unknown_Attribute", 53 | "flags" : "-O--CK", 54 | "id" : "166", 55 | "worst" : 100, 56 | "fail" : "-" 57 | }, 58 | { 59 | "id" : "167", 60 | "flags" : "-O--CK", 61 | "fail" : "-", 62 | "worst" : 100, 63 | "raw" : "46", 64 | "normalized" : 100, 65 | "value" : 100, 66 | "threshold" : 0, 67 | "name" : "Unknown_Attribute" 68 | }, 69 | { 70 | "name" : "Unknown_Attribute", 71 | "raw" : "5", 72 | "normalized" : 100, 73 | "value" : 100, 74 | "threshold" : 0, 75 | "fail" : "-", 76 | "worst" : 100, 77 | "id" : "168", 78 | "flags" : "-O--CK" 79 | }, 80 | { 81 | "flags" : "-O--CK", 82 | "id" : "169", 83 | "worst" : 100, 84 | "fail" : "-", 85 | "normalized" : 100, 86 | "value" : 100, 87 | "threshold" : 0, 88 | "raw" : "1079", 89 | "name" : "Unknown_Attribute" 90 | }, 91 | { 92 | "raw" : "0", 93 | "threshold" : 0, 94 | "normalized" : 100, 95 | "value" : 100, 96 | "name" : "Unknown_Attribute", 97 | "id" : "170", 98 | "flags" : "-O--CK", 99 | "fail" : "-", 100 | "worst" : 100 101 | }, 102 | { 103 | "fail" : "-", 104 | "worst" : 100, 105 | "id" : "171", 106 | "flags" : "-O--CK", 107 | "name" : "Unknown_Attribute", 108 | "raw" : "0", 109 | "normalized" : 100, 110 | "value" : 100, 111 | "threshold" : 0 112 | }, 113 | { 114 | "name" : "Unknown_Attribute", 115 | "raw" : "0", 116 | "normalized" : 100, 117 | "value" : 100, 118 | "threshold" : 0, 119 | "fail" : "-", 120 | "worst" : 100, 121 | "id" : "172", 122 | "flags" : "-O--CK" 123 | }, 124 | { 125 | "name" : "Unknown_Attribute", 126 | "threshold" : 0, 127 | "normalized" : 100, 128 | "value" : 100, 129 | "raw" : "1", 130 | "worst" : 100, 131 | "fail" : "-", 132 | "flags" : "-O--CK", 133 | "id" : "173" 134 | }, 135 | { 136 | "name" : "Unknown_Attribute", 137 | "normalized" : 100, 138 | "value" : 100, 139 | "threshold" : 0, 140 | "raw" : "22", 141 | "worst" : 100, 142 | "fail" : "-", 143 | "flags" : "-O--CK", 144 | "id" : "174" 145 | }, 146 | { 147 | "worst" : 100, 148 | "fail" : "-", 149 | "flags" : "-O--CK", 150 | "id" : "184", 151 | "name" : "End-to-End_Error", 152 | "normalized" : 100, 153 | "value" : 100, 154 | "threshold" : 0, 155 | "raw" : "0" 156 | }, 157 | { 158 | "name" : "Reported_Uncorrect", 159 | "normalized" : 100, 160 | "value" : 100, 161 | "threshold" : 0, 162 | "raw" : "0", 163 | "worst" : 100, 164 | "fail" : "-", 165 | "flags" : "-O--CK", 166 | "id" : "187" 167 | }, 168 | { 169 | "name" : "Command_Timeout", 170 | "raw" : "0", 171 | "normalized" : 100, 172 | "value" : 100, 173 | "threshold" : 0, 174 | "fail" : "-", 175 | "worst" : 100, 176 | "id" : "188", 177 | "flags" : "-O--CK" 178 | }, 179 | { 180 | "threshold" : 0, 181 | "normalized" : 73, 182 | "value" : 73, 183 | "raw" : "27 (Min/Max 23/64)", 184 | "name" : "Temperature_Celsius", 185 | "flags" : "-O---K", 186 | "id" : "194", 187 | "worst" : 64, 188 | "fail" : "-" 189 | }, 190 | { 191 | "worst" : 100, 192 | "fail" : "-", 193 | "flags" : "-O--CK", 194 | "id" : "199", 195 | "name" : "UDMA_CRC_Error_Count", 196 | "normalized" : 100, 197 | "value" : 100, 198 | "threshold" : 0, 199 | "raw" : "0" 200 | }, 201 | { 202 | "name" : "Unknown_SSD_Attribute", 203 | "threshold" : 0, 204 | "normalized" : 100, 205 | "value" : 100, 206 | "raw" : "146029805602", 207 | "worst" : 100, 208 | "fail" : "-", 209 | "flags" : "-O--CK", 210 | "id" : "230" 211 | }, 212 | { 213 | "raw" : "100", 214 | "normalized" : 100, 215 | "value" : 100, 216 | "threshold" : 4, 217 | "name" : "Available_Reservd_Space", 218 | "id" : "232", 219 | "flags" : "PO--CK", 220 | "fail" : "-", 221 | "worst" : 100 222 | }, 223 | { 224 | "threshold" : 0, 225 | "normalized" : 100, 226 | "value" : 100, 227 | "raw" : "1574", 228 | "name" : "Media_Wearout_Indicator", 229 | "flags" : "-O--CK", 230 | "id" : "233", 231 | "worst" : 100, 232 | "fail" : "-" 233 | }, 234 | { 235 | "id" : "234", 236 | "flags" : "-O--CK", 237 | "fail" : "-", 238 | "worst" : 100, 239 | "raw" : "2303", 240 | "normalized" : 100, 241 | "value" : 100, 242 | "threshold" : 0, 243 | "name" : "Unknown_Attribute" 244 | }, 245 | { 246 | "fail" : "-", 247 | "worst" : 253, 248 | "id" : "241", 249 | "flags" : "----CK", 250 | "name" : "Total_LBAs_Written", 251 | "raw" : "2111", 252 | "normalized" : 253, 253 | "value" : 253, 254 | "threshold" : 0 255 | }, 256 | { 257 | "worst" : 253, 258 | "fail" : "-", 259 | "flags" : "----CK", 260 | "id" : "242", 261 | "name" : "Total_LBAs_Read", 262 | "threshold" : 0, 263 | "normalized" : 253, 264 | "value" : 253, 265 | "raw" : "1542" 266 | }, 267 | { 268 | "name" : "Unknown_Attribute", 269 | "normalized" : 0, 270 | "value" : 0, 271 | "threshold" : 0, 272 | "raw" : "0", 273 | "worst" : 100, 274 | "fail" : "-", 275 | "flags" : "-O--CK", 276 | "id" : "244" 277 | } 278 | ], 279 | "type" : "ata", 280 | "health" : "PASSED" 281 | } 282 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sdd_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdd 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=SanDisk SD8SB8U1T001122 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_SERIAL=SANDISK_00000000000 8 | E: ID_SERIAL_SHORT=000000000000 9 | E: ID_TYPE=disk 10 | E: ID_WWN=0x0000000000000000 11 | 12 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sde/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sde/queue/rotational: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sde/size: -------------------------------------------------------------------------------- 1 | 1000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sde_smart: -------------------------------------------------------------------------------- 1 | smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.65-1-pve] (local build) 2 | Copyright (C) 2002-19, Bruce Allen, Christian Franke, www.smartmontools.org 3 | 4 | === START OF READ SMART DATA SECTION === 5 | SMART overall-health self-assessment test result: PASSED 6 | 7 | SMART Attributes Data Structure revision number: 10 8 | Vendor Specific SMART Attributes with Thresholds: 9 | ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 10 | 1 Raw_Read_Error_Rate -O--CK 120 120 050 - 0/0 11 | 5 Retired_Block_Count PO--CK 100 100 003 - 0 12 | 9 Power_On_Hours_and_Msec -O--CK 091 091 000 - 7963h+07m+54.620s 13 | 12 Power_Cycle_Count -O--CK 099 099 000 - 1153 14 | 171 Program_Fail_Count -O-R-- 100 100 000 - 0 15 | 172 Erase_Fail_Count -O--CK 100 100 000 - 0 16 | 174 Unexpect_Power_Loss_Ct ----CK 000 000 000 - 113 17 | 177 Wear_Range_Delta ------ 000 000 000 - 1 18 | 181 Program_Fail_Count -O-R-- 100 100 000 - 0 19 | 182 Erase_Fail_Count -O--CK 100 100 000 - 0 20 | 187 Reported_Uncorrect -O--C- 100 100 000 - 0 21 | 189 Airflow_Temperature_Cel ------ 027 049 000 - 27 (Min/Max 2/49) 22 | 194 Temperature_Celsius -O---K 027 049 000 - 27 (Min/Max 2/49) 23 | 195 ECC_Uncorr_Error_Count --SRC- 120 120 000 - 0/0 24 | 196 Reallocated_Event_Count PO--CK 100 100 003 - 0 25 | 201 Unc_Soft_Read_Err_Rate --SRC- 120 120 000 - 0/0 26 | 204 Soft_ECC_Correct_Rate --SRC- 120 120 000 - 0/0 27 | 230 Life_Curve_Status PO--C- 100 100 000 - 100 28 | 231 SSD_Life_Left ------ 091 091 011 - 4294967296 29 | 233 SandForce_Internal -O--CK 000 000 000 - 6317 30 | 234 SandForce_Internal -O--CK 000 000 000 - 4252 31 | 241 Lifetime_Writes_GiB -O--CK 000 000 000 - 4252 32 | 242 Lifetime_Reads_GiB -O--CK 000 000 000 - 34599 33 | 244 Unknown_Attribute ------ 099 099 010 - 4063273 34 | ||||||_ K auto-keep 35 | |||||__ C event count 36 | ||||___ R error rate 37 | |||____ S speed/performance 38 | ||_____ O updated online 39 | |______ P prefailure warning 40 | 41 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sde_smart_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "health": "PASSED", 3 | "type": "ata", 4 | "attributes": [ 5 | { 6 | "fail": "-", 7 | "id": " 1", 8 | "raw": "0/0", 9 | "flags": "-O--CK", 10 | "name": "Raw_Read_Error_Rate", 11 | "threshold": 50, 12 | "normalized": 120, 13 | "value": 120, 14 | "worst": 120 15 | }, 16 | { 17 | "id": " 5", 18 | "fail": "-", 19 | "normalized": 100, 20 | "value": 100, 21 | "worst": 100, 22 | "threshold": 3, 23 | "name": "Retired_Block_Count", 24 | "flags": "PO--CK", 25 | "raw": "0" 26 | }, 27 | { 28 | "fail": "-", 29 | "id": " 9", 30 | "raw": "7963h+07m+54.620s", 31 | "flags": "-O--CK", 32 | "worst": 91, 33 | "normalized": 91, 34 | "value": 91, 35 | "name": "Power_On_Hours_and_Msec", 36 | "threshold": 0 37 | }, 38 | { 39 | "id": " 12", 40 | "fail": "-", 41 | "threshold": 0, 42 | "name": "Power_Cycle_Count", 43 | "worst": 99, 44 | "normalized": 99, 45 | "value": 99, 46 | "flags": "-O--CK", 47 | "raw": "1153" 48 | }, 49 | { 50 | "flags": "-O-R--", 51 | "raw": "0", 52 | "worst": 100, 53 | "normalized": 100, 54 | "value": 100, 55 | "threshold": 0, 56 | "name": "Program_Fail_Count", 57 | "fail": "-", 58 | "id": "171" 59 | }, 60 | { 61 | "fail": "-", 62 | "id": "172", 63 | "flags": "-O--CK", 64 | "raw": "0", 65 | "name": "Erase_Fail_Count", 66 | "threshold": 0, 67 | "worst": 100, 68 | "normalized": 100, 69 | "value": 100 70 | }, 71 | { 72 | "fail": "-", 73 | "id": "174", 74 | "raw": "113", 75 | "flags": "----CK", 76 | "normalized": 0, 77 | "value": 0, 78 | "worst": 0, 79 | "threshold": 0, 80 | "name": "Unexpect_Power_Loss_Ct" 81 | }, 82 | { 83 | "id": "177", 84 | "fail": "-", 85 | "normalized": 0, 86 | "value": 0, 87 | "worst": 0, 88 | "name": "Wear_Range_Delta", 89 | "threshold": 0, 90 | "flags": "------", 91 | "raw": "1" 92 | }, 93 | { 94 | "flags": "-O-R--", 95 | "raw": "0", 96 | "threshold": 0, 97 | "name": "Program_Fail_Count", 98 | "worst": 100, 99 | "normalized": 100, 100 | "value": 100, 101 | "fail": "-", 102 | "id": "181" 103 | }, 104 | { 105 | "threshold": 0, 106 | "name": "Erase_Fail_Count", 107 | "normalized": 100, 108 | "value": 100, 109 | "worst": 100, 110 | "flags": "-O--CK", 111 | "raw": "0", 112 | "id": "182", 113 | "fail": "-" 114 | }, 115 | { 116 | "flags": "-O--C-", 117 | "raw": "0", 118 | "normalized": 100, 119 | "value": 100, 120 | "worst": 100, 121 | "threshold": 0, 122 | "name": "Reported_Uncorrect", 123 | "fail": "-", 124 | "id": "187" 125 | }, 126 | { 127 | "normalized": 27, 128 | "value": 27, 129 | "worst": 49, 130 | "name": "Airflow_Temperature_Cel", 131 | "threshold": 0, 132 | "flags": "------", 133 | "raw": "27 (Min/Max 2/49)", 134 | "id": "189", 135 | "fail": "-" 136 | }, 137 | { 138 | "threshold": 0, 139 | "name": "Temperature_Celsius", 140 | "worst": 49, 141 | "normalized": 27, 142 | "value": 27, 143 | "flags": "-O---K", 144 | "raw": "27 (Min/Max 2/49)", 145 | "id": "194", 146 | "fail": "-" 147 | }, 148 | { 149 | "id": "195", 150 | "fail": "-", 151 | "worst": 120, 152 | "normalized": 120, 153 | "value": 120, 154 | "threshold": 0, 155 | "name": "ECC_Uncorr_Error_Count", 156 | "raw": "0/0", 157 | "flags": "--SRC-" 158 | }, 159 | { 160 | "fail": "-", 161 | "id": "196", 162 | "raw": "0", 163 | "flags": "PO--CK", 164 | "threshold": 3, 165 | "name": "Reallocated_Event_Count", 166 | "normalized": 100, 167 | "value": 100, 168 | "worst": 100 169 | }, 170 | { 171 | "normalized": 120, 172 | "value": 120, 173 | "worst": 120, 174 | "threshold": 0, 175 | "name": "Unc_Soft_Read_Err_Rate", 176 | "flags": "--SRC-", 177 | "raw": "0/0", 178 | "id": "201", 179 | "fail": "-" 180 | }, 181 | { 182 | "raw": "0/0", 183 | "flags": "--SRC-", 184 | "normalized": 120, 185 | "value": 120, 186 | "worst": 120, 187 | "threshold": 0, 188 | "name": "Soft_ECC_Correct_Rate", 189 | "fail": "-", 190 | "id": "204" 191 | }, 192 | { 193 | "normalized": 100, 194 | "value": 100, 195 | "worst": 100, 196 | "threshold": 0, 197 | "name": "Life_Curve_Status", 198 | "raw": "100", 199 | "flags": "PO--C-", 200 | "id": "230", 201 | "fail": "-" 202 | }, 203 | { 204 | "id": "231", 205 | "fail": "-", 206 | "worst": 91, 207 | "normalized": 91, 208 | "value": 91, 209 | "name": "SSD_Life_Left", 210 | "threshold": 11, 211 | "flags": "------", 212 | "raw": "4294967296" 213 | }, 214 | { 215 | "raw": "6317", 216 | "flags": "-O--CK", 217 | "name": "SandForce_Internal", 218 | "threshold": 0, 219 | "normalized": 0, 220 | "value": 0, 221 | "worst": 0, 222 | "fail": "-", 223 | "id": "233" 224 | }, 225 | { 226 | "normalized": 0, 227 | "value": 0, 228 | "worst": 0, 229 | "name": "SandForce_Internal", 230 | "threshold": 0, 231 | "flags": "-O--CK", 232 | "raw": "4252", 233 | "id": "234", 234 | "fail": "-" 235 | }, 236 | { 237 | "worst": 0, 238 | "normalized": 0, 239 | "value": 0, 240 | "name": "Lifetime_Writes_GiB", 241 | "threshold": 0, 242 | "flags": "-O--CK", 243 | "raw": "4252", 244 | "id": "241", 245 | "fail": "-" 246 | }, 247 | { 248 | "flags": "-O--CK", 249 | "raw": "34599", 250 | "normalized": 0, 251 | "value": 0, 252 | "worst": 0, 253 | "threshold": 0, 254 | "name": "Lifetime_Reads_GiB", 255 | "fail": "-", 256 | "id": "242" 257 | }, 258 | { 259 | "threshold": 10, 260 | "name": "Unknown_Attribute", 261 | "worst": 99, 262 | "normalized": 99, 263 | "value": 99, 264 | "flags": "------", 265 | "raw": "4063273", 266 | "id": "244", 267 | "fail": "-" 268 | } 269 | ] 270 | } 271 | -------------------------------------------------------------------------------- /src/test/disk_tests/ssd_smart/sde_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sde 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=KINGSTON SHFS37A120G 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_SERIAL=KINGSTON_00000000000 8 | E: ID_SERIAL_SHORT=000000000000 9 | E: ID_TYPE=disk 10 | E: ID_WWN=0x0000000000000000 11 | 12 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/disklist: -------------------------------------------------------------------------------- 1 | sda 2 | sdb 3 | sdc 4 | sdd 5 | sde 6 | sdf 7 | sdg 8 | sdh 9 | sdi 10 | sdj 11 | sdk 12 | sdl 13 | sdm 14 | sdn 15 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/disklist_expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdf" : { 3 | "gpt" : 1, 4 | "rpm" : 0, 5 | "size" : 1536000, 6 | "type" : "hdd", 7 | "osdencrypted": 0, 8 | "osdid" : "444", 9 | "osdid-list" : ["444"], 10 | "bluestore" : "0", 11 | "health" : "UNKNOWN", 12 | "model" : "MODEL1", 13 | "used" : "mounted", 14 | "wearout" : "N/A", 15 | "wwn" : "0x0000000000000000", 16 | "devpath" : "/dev/sdf", 17 | "vendor" : "ATA", 18 | "serial" : "SERIAL1" 19 | }, 20 | "sde" : { 21 | "wwn" : "0x0000000000000000", 22 | "devpath" : "/dev/sde", 23 | "serial" : "SERIAL1", 24 | "vendor" : "ATA", 25 | "health" : "UNKNOWN", 26 | "rpm" : 0, 27 | "size" : 1536000, 28 | "gpt" : 1, 29 | "osdid" : -1, 30 | "osdid-list" : null, 31 | "type" : "hdd", 32 | "model" : "MODEL1", 33 | "used" : "Device Mapper", 34 | "wearout" : "N/A" 35 | }, 36 | "sdb" : { 37 | "serial" : "SERIAL1", 38 | "vendor" : "ATA", 39 | "wwn" : "0x0000000000000000", 40 | "devpath" : "/dev/sdb", 41 | "model" : "MODEL1", 42 | "used" : "LVM", 43 | "wearout" : "N/A", 44 | "health" : "UNKNOWN", 45 | "gpt" : 1, 46 | "size" : 1536000, 47 | "rpm" : 0, 48 | "type" : "hdd", 49 | "osdid" : -1, 50 | "osdid-list" : null 51 | }, 52 | "sda" : { 53 | "model" : "MODEL1", 54 | "used" : "mounted", 55 | "mounted": 1, 56 | "wearout" : "N/A", 57 | "health" : "UNKNOWN", 58 | "gpt" : 1, 59 | "size" : 1536000, 60 | "rpm" : 0, 61 | "type" : "hdd", 62 | "osdid" : -1, 63 | "osdid-list" : null, 64 | "serial" : "SERIAL1", 65 | "vendor" : "ATA", 66 | "wwn" : "0x0000000000000000", 67 | "devpath" : "/dev/sda" 68 | }, 69 | "sdc" : { 70 | "serial" : "SERIAL1", 71 | "vendor" : "ATA", 72 | "devpath" : "/dev/sdc", 73 | "wwn" : "0x0000000000000000", 74 | "used" : "ZFS", 75 | "wearout" : "N/A", 76 | "model" : "MODEL1", 77 | "health" : "UNKNOWN", 78 | "type" : "hdd", 79 | "osdid" : -1, 80 | "osdid-list" : null, 81 | "gpt" : 1, 82 | "rpm" : 0, 83 | "size" : 1536000 84 | }, 85 | "sdd" : { 86 | "model" : "MODEL1", 87 | "wearout" : "N/A", 88 | "health" : "UNKNOWN", 89 | "size" : 1536000, 90 | "rpm" : 0, 91 | "gpt" : 1, 92 | "type" : "hdd", 93 | "osdid" : -1, 94 | "osdid-list" : null, 95 | "serial" : "SERIAL1", 96 | "used": "ZFS", 97 | "vendor" : "ATA", 98 | "wwn" : "0x0000000000000000", 99 | "devpath" : "/dev/sdd" 100 | }, 101 | "sdg" : { 102 | "serial" : "SERIAL1", 103 | "vendor" : "ATA", 104 | "wwn" : "0x0000000000000000", 105 | "devpath" : "/dev/sdg", 106 | "model" : "MODEL1", 107 | "used" : "LVM", 108 | "wearout" : "N/A", 109 | "health" : "UNKNOWN", 110 | "gpt" : 1, 111 | "size" : 1536000, 112 | "rpm" : 0, 113 | "type" : "hdd", 114 | "bluestore": 1, 115 | "osdencrypted": 0, 116 | "osdid" : 1, 117 | "osdid-list" : [1] 118 | }, 119 | "sdh" : { 120 | "serial" : "SERIAL1", 121 | "vendor" : "ATA", 122 | "wwn" : "0x0000000000000000", 123 | "devpath" : "/dev/sdh", 124 | "model" : "MODEL1", 125 | "used" : "LVM", 126 | "wearout" : "N/A", 127 | "health" : "UNKNOWN", 128 | "gpt" : 1, 129 | "journals": 1, 130 | "size" : 1536000, 131 | "rpm" : 0, 132 | "type" : "hdd", 133 | "osdid" : -1, 134 | "osdid-list" : null 135 | }, 136 | "sdi" : { 137 | "serial" : "SERIAL1", 138 | "vendor" : "ATA", 139 | "wwn" : "0x0000000000000000", 140 | "devpath" : "/dev/sdi", 141 | "model" : "MODEL1", 142 | "used" : "LVM", 143 | "wearout" : "N/A", 144 | "health" : "UNKNOWN", 145 | "gpt" : 1, 146 | "size" : 1536000, 147 | "rpm" : 0, 148 | "type" : "hdd", 149 | "db": 1, 150 | "osdid" : -1, 151 | "osdid-list" : null 152 | }, 153 | "sdj" : { 154 | "serial" : "SERIAL1", 155 | "vendor" : "ATA", 156 | "wwn" : "0x0000000000000000", 157 | "devpath" : "/dev/sdj", 158 | "model" : "MODEL1", 159 | "used" : "LVM", 160 | "wearout" : "N/A", 161 | "health" : "UNKNOWN", 162 | "gpt" : 1, 163 | "size" : 1536000, 164 | "rpm" : 0, 165 | "bluestore": 0, 166 | "type" : "hdd", 167 | "osdencrypted": 1, 168 | "osdid" : 0, 169 | "osdid-list" : [0] 170 | }, 171 | "sdk" : { 172 | "serial" : "SERIAL1", 173 | "vendor" : "ATA", 174 | "wwn" : "0x0000000000000000", 175 | "devpath" : "/dev/sdk", 176 | "model" : "MODEL1", 177 | "used" : "LVM", 178 | "wearout" : "N/A", 179 | "health" : "UNKNOWN", 180 | "gpt" : 1, 181 | "size" : 1536000, 182 | "rpm" : 0, 183 | "bluestore": 0, 184 | "type" : "hdd", 185 | "osdencrypted": 0, 186 | "osdid" : 230, 187 | "osdid-list": [230] 188 | }, 189 | "sdl" : { 190 | "serial" : "SERIAL1", 191 | "vendor" : "ATA", 192 | "wwn" : "0x0000000000000000", 193 | "devpath" : "/dev/sdl", 194 | "model" : "MODEL1", 195 | "used" : "LVM", 196 | "wearout" : "N/A", 197 | "health" : "UNKNOWN", 198 | "gpt" : 1, 199 | "size" : 1536000, 200 | "rpm" : 0, 201 | "type" : "hdd", 202 | "osdid" : -1, 203 | "osdid-list" : null 204 | }, 205 | "sdm" : { 206 | "serial" : "SERIAL1", 207 | "vendor" : "ATA", 208 | "wwn" : "0x0000000000000000", 209 | "devpath" : "/dev/sdm", 210 | "model" : "MODEL1", 211 | "used" : "ZFS", 212 | "wearout" : "N/A", 213 | "health" : "UNKNOWN", 214 | "gpt" : 1, 215 | "size" : 1536000, 216 | "rpm" : 0, 217 | "type" : "hdd", 218 | "osdid" : -1, 219 | "osdid-list" : null 220 | }, 221 | "sdn" : { 222 | "serial" : "SERIAL1", 223 | "vendor" : "ATA", 224 | "wwn" : "0x0000000000000000", 225 | "devpath" : "/dev/sdn", 226 | "model" : "MODEL1", 227 | "used" : "xfs", 228 | "wearout" : "N/A", 229 | "health" : "UNKNOWN", 230 | "gpt" : 0, 231 | "size" : 1536000, 232 | "rpm" : 0, 233 | "type" : "hdd", 234 | "osdid" : -1, 235 | "osdid-list" : null 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/lsblk: -------------------------------------------------------------------------------- 1 | { 2 | "blockdevices": [ 3 | {"path":"/dev/sdm", "parttype":null, "fstype":null}, 4 | {"path":"/dev/sdm1", "parttype":"6a898cc3-1dd2-11b2-99a6-080020736631", "fstype":"zfs_member"}, 5 | {"path":"/dev/sdm9", "parttype":"6a945a3b-1dd2-11b2-99a6-080020736631", "fstype":null}, 6 | {"path":"/dev/sdn", "parttype":null, "fstype":"xfs"} 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/lvs: -------------------------------------------------------------------------------- 1 | /dev/sdg(0);osd-block-01234;ceph.osd_id=1 2 | /dev/sdh(0);osd-journal-01234;ceph.osd_id=1 3 | /dev/sdi(0);osd-db-01234;ceph.osd_id=1,dasdf 4 | /dev/sdj(0);osd-data-01234;ceph.osd_id=0,asfd,ceph.encrypted=1 5 | /dev/sdk(0);osd-data-231231;ceph.osd_id=230,ceph.fsid=test 6 | /dev/sdl(0);osd-data-234132;ceph.osd_id=,bar 7 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/mounts: -------------------------------------------------------------------------------- 1 | /dev/sda /mountpoint1 2 | /dev/sdf1 /var/lib/ceph/osd/ceph-444 3 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/partlist: -------------------------------------------------------------------------------- 1 | sdd1 2 | sdd2 3 | sde1 4 | sdf1 5 | sdm1 6 | sdm9 7 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/pvs: -------------------------------------------------------------------------------- 1 | /dev/sdb 2 | /dev/sdg 3 | /dev/sdh 4 | /dev/sdi 5 | /dev/sdj 6 | /dev/sdk 7 | /dev/sdl 8 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sda/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sda/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sda/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sda_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sda 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdb/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdb/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdb/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdb_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdb 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdc/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdc/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdc/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdc_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdc 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdd/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdd/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdd/sdd1/size: -------------------------------------------------------------------------------- 1 | 1000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdd/sdd2/size: -------------------------------------------------------------------------------- 1 | 2000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdd/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdd_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdd 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sde/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sde/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sde/sde1/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sde/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sde_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sde 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdf/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdf/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdf/sdf1/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdf/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdf_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdf 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdg/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdg/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdg/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdg_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdg 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdh/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdh/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdh/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdh_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdh 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdi/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdi/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdi/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdi_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdi 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdj/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdj/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdj/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdj_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdj 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdk/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdk/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdk/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdk_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdk 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdl/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdl/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdl/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdl_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdl 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdm/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdm/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdm/sdm1/size: -------------------------------------------------------------------------------- 1 | 1000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdm/sdm9/size: -------------------------------------------------------------------------------- 1 | 2000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdm/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdm_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdm 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_PART_TABLE_TYPE=gpt 7 | E: ID_PART_TABLE_UUID=8417b93f-eff9-4e8f-8d84-dc2e77fc07a2 8 | E: ID_SERIAL=SERIAL1 9 | E: ID_SERIAL_SHORT=SERIAL1 10 | E: ID_TYPE=disk 11 | E: ID_WWN=0x0000000000000000 12 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 13 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdn/device/vendor: -------------------------------------------------------------------------------- 1 | ATA 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdn/queue/rotational: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdn/size: -------------------------------------------------------------------------------- 1 | 3000 2 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/sdn_udevadm: -------------------------------------------------------------------------------- 1 | E: DEVNAME=/dev/sdn 2 | E: DEVTYPE=disk 3 | E: ID_ATA_ROTATION_RATE_RPM=0 4 | E: ID_BUS=ata 5 | E: ID_MODEL=MODEL1 6 | E: ID_SERIAL=SERIAL1 7 | E: ID_SERIAL_SHORT=SERIAL1 8 | E: ID_TYPE=disk 9 | E: ID_FS_UUID=ab54fba8-48fe-4d37-bbe7-b403f94d3bed 10 | E: ID_FS_UUID_ENC=ab54fba8-48fe-4d37-bbe7-b403f94d3bed 11 | E: ID_FS_TYPE=xfs 12 | E: ID_FS_USAGE=filesystem 13 | E: ID_WWN=0x0000000000000000 14 | E: ID_WWN_WITH_EXTENSION=0x0000000000000000 15 | -------------------------------------------------------------------------------- /src/test/disk_tests/usages/zpool: -------------------------------------------------------------------------------- 1 | testpool 2.00T 1.00T 2.60T - 5% 28% 1.00x ONLINE - 2 | /dev/sdc 3.62T 1.02T 2.60T - 5% 28% 3 | log - - - - - - 4 | /dev/sdd1 15.9G 2.79M 15.9G - 82% 0% 5 | cache - - - - - - 6 | /dev/sdd2 42.5G 36.2G 6.36G - 0% 85% 7 | -------------------------------------------------------------------------------- /src/test/disklist_test.pm: -------------------------------------------------------------------------------- 1 | package PVE::Diskmanage::Test; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib qw(..); 7 | 8 | use PVE::Diskmanage; 9 | use PVE::Tools; 10 | 11 | use Test::MockModule; 12 | use Test::More; 13 | use JSON; 14 | use Data::Dumper; 15 | 16 | my $testcasedir; # current case directory 17 | my $testcount = 0; # testcount for TAP::Harness 18 | my $diskmanage_module; # mockmodule for PVE::Diskmanage 19 | my $print = 0; 20 | 21 | sub mocked_run_command { 22 | my ($cmd, %param) = @_; 23 | 24 | my $outputlines = []; 25 | if (my $ref = ref($cmd)) { 26 | if ($cmd->[0] =~ m/udevadm/i) { 27 | # simulate udevadm output 28 | my $dev = $cmd->[3]; 29 | $dev =~ s|/sys/block/||; 30 | @$outputlines = split(/\n/, read_test_file("${dev}_udevadm")); 31 | 32 | } elsif ($cmd->[0] =~ m/smartctl/i) { 33 | # simulate smartctl output 34 | my $dev; 35 | my $type; 36 | if (@$cmd > 3) { 37 | $dev = $cmd->[5]; 38 | $type = 'smart'; 39 | } else { 40 | $dev = $cmd->[2]; 41 | $type = 'health'; 42 | } 43 | $dev =~ s|/dev/||; 44 | @$outputlines = split(/\n/, read_test_file("${dev}_${type}")); 45 | } elsif ($cmd->[0] =~ m/sgdisk/i) { 46 | # simulate sgdisk 47 | die "implement me: @$cmd\n"; 48 | } elsif ($cmd->[0] =~ m/zpool/i) { 49 | # simulate zpool output 50 | @$outputlines = split(/\n/, read_test_file('zpool')); 51 | 52 | } elsif ($cmd->[0] =~ m/pvs/i) { 53 | # simulate lvs output 54 | @$outputlines = split(/\n/, read_test_file('pvs')); 55 | } elsif ($cmd->[0] =~ m/lvs/i) { 56 | @$outputlines = split(/\n/, read_test_file('lvs')); 57 | } elsif ($cmd->[0] =~ m/lsblk/i) { 58 | my $content = read_test_file('lsblk'); 59 | if ($content eq '') { 60 | $content = '{}'; 61 | } 62 | @$outputlines = split(/\n/, $content); 63 | } else { 64 | die "unexpected run_command call: '@$cmd', aborting\n"; 65 | } 66 | } else { 67 | print "unexpected run_command call: '@$cmd', aborting\n"; 68 | die; 69 | } 70 | 71 | my $outfunc; 72 | if ($param{outfunc}) { 73 | $outfunc = $param{outfunc}; 74 | map { &$outfunc(($_)) } @$outputlines; 75 | 76 | return 0; 77 | } 78 | } 79 | 80 | sub mocked_get_sysdir_info { 81 | my ($param) = @_; 82 | 83 | my $originalsub = $diskmanage_module->original('get_sysdir_info'); 84 | 85 | $param =~ s|/sys/block|disk_tests/$testcasedir|; 86 | 87 | return &$originalsub($param); 88 | } 89 | 90 | sub mocked_get_sysdir_size { 91 | my ($param) = @_; 92 | 93 | my $originalsub = $diskmanage_module->original('get_sysdir_size'); 94 | 95 | $param =~ s|/sys/block|disk_tests/$testcasedir|; 96 | 97 | return &$originalsub($param); 98 | } 99 | 100 | sub mocked_is_iscsi { 101 | return 0; 102 | } 103 | 104 | sub mocked_dir_glob_foreach { 105 | my ($dir, $regex, $sub) = @_; 106 | 107 | my $lines = []; 108 | 109 | # read lines in from file 110 | if ($dir =~ m{^/sys/block$} ) { 111 | @$lines = split(/\n/, read_test_file('disklist')); 112 | } elsif ($dir =~ m{^/sys/block/([^/]+)}) { 113 | @$lines = split(/\n/, read_test_file('partlist')); 114 | } 115 | 116 | foreach my $line (@$lines) { 117 | if ($line =~ m/$regex/) { 118 | &$sub($line); 119 | } 120 | } 121 | } 122 | 123 | sub mocked_parse_proc_mounts { 124 | my $text = read_test_file('mounts'); 125 | 126 | my $mounts = []; 127 | 128 | foreach my $line(split(/\n/, $text)) { 129 | push @$mounts, [split(/\s+/, $line)]; 130 | } 131 | 132 | return $mounts; 133 | } 134 | 135 | sub read_test_file { 136 | my ($filename) = @_; 137 | 138 | if (!-f "disk_tests/$testcasedir/$filename") { 139 | print "file '$testcasedir/$filename' not found\n"; 140 | return ''; 141 | } 142 | open (my $fh, '<', "disk_tests/$testcasedir/$filename") 143 | or die "Cannot open disk_tests/$testcasedir/$filename: $!"; 144 | 145 | my $output = <$fh> // ''; 146 | chomp $output if $output; 147 | while (my $line = <$fh>) { 148 | chomp $line; 149 | $output .= "\n$line"; 150 | } 151 | 152 | return $output; 153 | } 154 | 155 | 156 | sub test_disk_list { 157 | my ($testdir) = @_; 158 | subtest "Test '$testdir'" => sub { 159 | my $testcount = 0; 160 | $testcasedir = $testdir; 161 | 162 | my $disks; 163 | my $expected_disk_list; 164 | eval { 165 | $disks = PVE::Diskmanage::get_disks(); 166 | }; 167 | warn $@ if $@; 168 | $expected_disk_list = decode_json(read_test_file('disklist_expected.json')); 169 | 170 | print Dumper($disks) if $print; 171 | $testcount++; 172 | is_deeply($disks, $expected_disk_list, 'disk list should be the same'); 173 | 174 | foreach my $disk (sort keys %$disks) { 175 | my $smart; 176 | my $expected_smart; 177 | eval { 178 | $smart = PVE::Diskmanage::get_smart_data("/dev/$disk"); 179 | print Dumper($smart) if $print; 180 | $expected_smart = decode_json(read_test_file("${disk}_smart_expected.json")); 181 | }; 182 | 183 | if ($smart && $expected_smart) { 184 | $testcount++; 185 | is_deeply($smart, $expected_smart, "smart data for '$disk' should be the same"); 186 | } elsif ($smart && -f "disk_tests/$testcasedir/${disk}_smart_expected.json") { 187 | $testcount++; 188 | ok(0, "could not parse expected smart for '$disk'\n"); 189 | } 190 | my $disk_tmp = {}; 191 | 192 | # test single disk parameter 193 | $disk_tmp = PVE::Diskmanage::get_disks($disk); 194 | warn $@ if $@; 195 | $testcount++; 196 | print Dumper $disk_tmp if $print; 197 | is_deeply($disk_tmp->{$disk}, $expected_disk_list->{$disk}, "disk $disk should be the same"); 198 | 199 | 200 | # test wrong parameter 201 | eval { 202 | PVE::Diskmanage::get_disks( { test => 1 } ); 203 | }; 204 | my $err = $@; 205 | $testcount++; 206 | is_deeply($err, "disks is not a string or array reference\n", "error message should be the same"); 207 | 208 | } 209 | # test multi disk parameter 210 | $disks = PVE::Diskmanage::get_disks( [ keys %$disks ] ); 211 | $testcount++; 212 | is_deeply($disks, $expected_disk_list, 'disk list should be the same'); 213 | 214 | done_testing($testcount); 215 | }; 216 | } 217 | 218 | # start reading tests: 219 | 220 | if (@ARGV && $ARGV[0] eq 'print') { 221 | $print = 1; 222 | } 223 | 224 | print("Setting up Mocking\n"); 225 | $diskmanage_module = Test::MockModule->new('PVE::Diskmanage', no_auto => 1); 226 | $diskmanage_module->mock('run_command' => \&mocked_run_command); 227 | print("\tMocked run_command\n"); 228 | $diskmanage_module->mock('dir_glob_foreach' => \&mocked_dir_glob_foreach); 229 | print("\tMocked dir_glob_foreach\n"); 230 | $diskmanage_module->mock('get_sysdir_info' => \&mocked_get_sysdir_info); 231 | print("\tMocked get_sysdir_info\n"); 232 | $diskmanage_module->mock('get_sysdir_size' => \&mocked_get_sysdir_size); 233 | print("\tMocked get_sysdir_size\n"); 234 | $diskmanage_module->mock('is_iscsi' => \&mocked_is_iscsi); 235 | print("\tMocked is_iscsi\n"); 236 | $diskmanage_module->mock('assert_blockdev' => sub { return 1; }); 237 | print("\tMocked assert_blockdev\n"); 238 | $diskmanage_module->mock('dir_is_empty' => sub { 239 | # all partitions have a holder dir 240 | my $val = shift; 241 | if ($val =~ m|^/sys/block/.+/.+/|) { 242 | return 0; 243 | } 244 | return 1; 245 | }); 246 | print("\tMocked dir_is_empty\n"); 247 | $diskmanage_module->mock('check_bin' => sub { return 1; }); 248 | print("\tMocked check_bin\n"); 249 | my $tools_module= Test::MockModule->new('PVE::ProcFSTools', no_auto => 1); 250 | $tools_module->mock('parse_proc_mounts' => \&mocked_parse_proc_mounts); 251 | print("\tMocked parse_proc_mounts\n"); 252 | print("Done Setting up Mocking\n\n"); 253 | 254 | print("Beginning Tests:\n\n"); 255 | opendir (my $dh, 'disk_tests') 256 | or die "Cannot open disk_tests: $!"; 257 | 258 | while (readdir $dh) { 259 | my $dir = $_; 260 | next if $dir eq '.' or $dir eq '..'; 261 | $testcount++; 262 | test_disk_list($dir); 263 | } 264 | 265 | done_testing($testcount); 266 | -------------------------------------------------------------------------------- /src/test/filesystem_path_test.pm: -------------------------------------------------------------------------------- 1 | package PVE::Storage::TestFilesystemPath; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib qw(..); 7 | 8 | use PVE::Storage; 9 | use Test::More; 10 | 11 | my $path = '/some/path'; 12 | 13 | # each array entry is a test that consists of the following keys: 14 | # volname => image name that is passed to parse_volname 15 | # snapname => to test the die condition 16 | # expected => the array of return values; or the die message 17 | my $tests = [ 18 | { 19 | volname => '1234/vm-1234-disk-0.raw', 20 | snapname => undef, 21 | expected => [ 22 | "$path/images/1234/vm-1234-disk-0.raw", 23 | '1234', 24 | 'images' 25 | ], 26 | }, 27 | { 28 | volname => '1234/vm-1234-disk-0.raw', 29 | snapname => 'my_snap', 30 | expected => "can't snapshot this image format\n" 31 | }, 32 | { 33 | volname => '1234/vm-1234-disk-0.qcow2', 34 | snapname => undef, 35 | expected => [ 36 | "$path/images/1234/vm-1234-disk-0.qcow2", 37 | '1234', 38 | 'images' 39 | ], 40 | }, 41 | { 42 | volname => '1234/vm-1234-disk-0.qcow2', 43 | snapname => 'my_snap', 44 | expected => [ 45 | "$path/images/1234/vm-1234-disk-0.qcow2", 46 | '1234', 47 | 'images' 48 | ], 49 | }, 50 | { 51 | volname => 'iso/my-awesome-proxmox.iso', 52 | snapname => undef, 53 | expected => [ 54 | "$path/template/iso/my-awesome-proxmox.iso", 55 | undef, 56 | 'iso' 57 | ], 58 | }, 59 | { 60 | volname => "backup/vzdump-qemu-1234-2020_03_30-21_12_40.vma", 61 | snapname => undef, 62 | expected => [ 63 | "$path/dump/vzdump-qemu-1234-2020_03_30-21_12_40.vma", 64 | 1234, 65 | 'backup' 66 | ], 67 | }, 68 | ]; 69 | 70 | plan tests => scalar @$tests; 71 | 72 | foreach my $tt (@$tests) { 73 | my $volname = $tt->{volname}; 74 | my $snapname = $tt->{snapname}; 75 | my $expected = $tt->{expected}; 76 | my $scfg = { path => $path }; 77 | my $got; 78 | 79 | eval { 80 | $got = [ PVE::Storage::Plugin->filesystem_path($scfg, $volname, $snapname) ]; 81 | }; 82 | $got = $@ if $@; 83 | 84 | is_deeply($got, $expected, "wantarray: filesystem_path for $volname") 85 | || diag(explain($got)); 86 | 87 | } 88 | 89 | done_testing(); 90 | 91 | 1; 92 | -------------------------------------------------------------------------------- /src/test/get_subdir_test.pm: -------------------------------------------------------------------------------- 1 | package PVE::Storage::TestGetSubdir; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib qw(..); 7 | 8 | use PVE::Storage::Plugin; 9 | use Test::More; 10 | 11 | my $scfg_with_path = { path => '/some/path' }; 12 | my $vtype_subdirs = PVE::Storage::Plugin::get_vtype_subdirs(); 13 | 14 | # each test is comprised of the following array keys: 15 | # [0] => storage config; positive with path key 16 | # [1] => storage type; see $vtype_subdirs 17 | # [2] => expected return from get_subdir 18 | my $tests = [ 19 | # failed matches 20 | [ $scfg_with_path, 'none', "unknown vtype 'none'\n" ], 21 | [ {}, 'iso', "storage definition has no path\n" ], 22 | ]; 23 | 24 | # creates additional positive tests 25 | foreach my $type (keys %$vtype_subdirs) { 26 | my $path = "$scfg_with_path->{path}/$vtype_subdirs->{$type}"; 27 | push @$tests, [ $scfg_with_path, $type, $path ]; 28 | } 29 | 30 | # creates additional tests for overrides 31 | foreach my $type (keys %$vtype_subdirs) { 32 | my $override = "${type}_override"; 33 | my $scfg_with_override = { path => '/some/path', 'content-dirs' => { $type => $override } }; 34 | push @$tests, [ $scfg_with_override, $type, "$scfg_with_override->{path}/$scfg_with_override->{'content-dirs'}->{$type}" ]; 35 | } 36 | 37 | plan tests => scalar @$tests; 38 | 39 | foreach my $tt (@$tests) { 40 | my ($scfg, $type, $expected) = @$tt; 41 | 42 | my $got; 43 | eval { $got = PVE::Storage::Plugin->get_subdir($scfg, $type) }; 44 | $got = $@ if $@; 45 | 46 | is ($got, $expected, "get_subdir for $type") || diag(explain($got)); 47 | } 48 | 49 | done_testing(); 50 | 51 | 1; 52 | -------------------------------------------------------------------------------- /src/test/ovf_manifests/Win10-Liz-disk1.vmdk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-storage/b6d049b176a6936e4d71a51c4d9de3e4f5680a82/src/test/ovf_manifests/Win10-Liz-disk1.vmdk -------------------------------------------------------------------------------- /src/test/ovf_manifests/Win10-Liz.ovf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Virtual disk information 9 | 10 | 11 | 12 | The list of logical networks 13 | 14 | The bridged network 15 | 16 | 17 | 18 | A virtual machine 19 | Win10-Liz 20 | 21 | The kind of installed guest operating system 22 | 23 | 24 | Virtual hardware requirements 25 | 26 | Virtual Hardware Family 27 | 0 28 | Win10-Liz 29 | vmx-11 30 | 31 | 32 | hertz * 10^6 33 | Number of Virtual CPUs 34 | 4 virtual CPU(s) 35 | 1 36 | 3 37 | 4 38 | 39 | 40 | byte * 2^20 41 | Memory Size 42 | 6144MB of memory 43 | 2 44 | 4 45 | 6144 46 | 47 | 48 | 0 49 | SATA Controller 50 | sataController0 51 | 3 52 | vmware.sata.ahci 53 | 20 54 | 55 | 56 | 0 57 | USB Controller (XHCI) 58 | usb3 59 | 4 60 | vmware.usb.xhci 61 | 23 62 | 63 | 64 | 0 65 | USB Controller (EHCI) 66 | usb 67 | 5 68 | vmware.usb.ehci 69 | 23 70 | 71 | 72 | 73 | 0 74 | SCSI Controller 75 | scsiController0 76 | 6 77 | lsilogicsas 78 | 6 79 | 80 | 81 | true 82 | serial0 83 | 7 84 | 21 85 | 86 | 87 | 88 | 0 89 | disk0 90 | ovf:/disk/vmdisk1 91 | 8 92 | 6 93 | 17 94 | 95 | 96 | 2 97 | true 98 | bridged 99 | E1000e ethernet adapter on "bridged" 100 | ethernet0 101 | 9 102 | E1000e 103 | 10 104 | 105 | 106 | 107 | false 108 | sound 109 | 10 110 | vmware.soundcard.hdaudio 111 | 1 112 | 113 | 114 | false 115 | video 116 | 11 117 | 24 118 | 119 | 120 | 121 | false 122 | vmci 123 | 12 124 | vmware.vmci 125 | 1 126 | 127 | 128 | 1 129 | false 130 | cdrom0 131 | 13 132 | 3 133 | 15 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /src/test/ovf_manifests/Win_2008_R2_two-disks.ovf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Virtual disk information 10 | 11 | 12 | 13 | 14 | The list of logical networks 15 | 16 | The bridged network 17 | 18 | 19 | 20 | A virtual machine 21 | Win_2008-R2x64 22 | 23 | The kind of installed guest operating system 24 | 25 | 26 | Virtual hardware requirements 27 | 28 | Virtual Hardware Family 29 | 0 30 | Win_2008-R2x64 31 | vmx-11 32 | 33 | 34 | hertz * 10^6 35 | Number of Virtual CPUs 36 | 1 virtual CPU(s) 37 | 1 38 | 3 39 | 1 40 | 41 | 42 | byte * 2^20 43 | Memory Size 44 | 2048MB of memory 45 | 2 46 | 4 47 | 2048 48 | 49 | 50 | 0 51 | SATA Controller 52 | sataController0 53 | 3 54 | vmware.sata.ahci 55 | 20 56 | 57 | 58 | 0 59 | USB Controller (EHCI) 60 | usb 61 | 4 62 | vmware.usb.ehci 63 | 23 64 | 65 | 66 | 67 | 0 68 | SCSI Controller 69 | scsiController0 70 | 5 71 | lsilogicsas 72 | 6 73 | 74 | 75 | true 76 | serial0 77 | 6 78 | 21 79 | 80 | 81 | 82 | 0 83 | disk0 84 | ovf:/disk/vmdisk1 85 | 7 86 | 5 87 | 17 88 | 89 | 90 | 1 91 | disk1 92 | ovf:/disk/vmdisk2 93 | 8 94 | 5 95 | 17 96 | 97 | 98 | 2 99 | true 100 | bridged 101 | E1000 ethernet adapter on "bridged" 102 | ethernet0 103 | 9 104 | E1000 105 | 10 106 | 107 | 108 | 109 | false 110 | sound 111 | 10 112 | vmware.soundcard.hdaudio 113 | 1 114 | 115 | 116 | false 117 | video 118 | 11 119 | 24 120 | 121 | 122 | 123 | false 124 | vmci 125 | 12 126 | vmware.vmci 127 | 1 128 | 129 | 130 | 1 131 | false 132 | cdrom0 133 | 13 134 | 3 135 | 15 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /src/test/ovf_manifests/disk1.vmdk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-storage/b6d049b176a6936e4d71a51c4d9de3e4f5680a82/src/test/ovf_manifests/disk1.vmdk -------------------------------------------------------------------------------- /src/test/ovf_manifests/disk2.vmdk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-storage/b6d049b176a6936e4d71a51c4d9de3e4f5680a82/src/test/ovf_manifests/disk2.vmdk -------------------------------------------------------------------------------- /src/test/run_disk_tests.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use TAP::Harness; 7 | 8 | my $harness = TAP::Harness->new( { verbosity => -2 }); 9 | my $res = $harness->runtests( "disklist_test.pm" ); 10 | 11 | exit -1 if !$res || $res->{failed} || $res->{parse_errors}; 12 | 13 | -------------------------------------------------------------------------------- /src/test/run_ovf_tests.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib qw(..); # prepend .. to @INC so we use the local version of PVE packages 6 | 7 | use FindBin '$Bin'; 8 | use PVE::GuestImport::OVF; 9 | use Test::More; 10 | 11 | use Data::Dumper; 12 | 13 | my $test_manifests = join ('/', $Bin, 'ovf_manifests'); 14 | 15 | print "parsing ovfs\n"; 16 | 17 | my $win2008 = eval { PVE::GuestImport::OVF::parse_ovf("$test_manifests/Win_2008_R2_two-disks.ovf") }; 18 | if (my $err = $@) { 19 | fail('parse win2008'); 20 | warn("error: $err\n"); 21 | } else { 22 | ok('parse win2008'); 23 | } 24 | my $win10 = eval { PVE::GuestImport::OVF::parse_ovf("$test_manifests/Win10-Liz.ovf") }; 25 | if (my $err = $@) { 26 | fail('parse win10'); 27 | warn("error: $err\n"); 28 | } else { 29 | ok('parse win10'); 30 | } 31 | my $win10noNs = eval { PVE::GuestImport::OVF::parse_ovf("$test_manifests/Win10-Liz_no_default_ns.ovf") }; 32 | if (my $err = $@) { 33 | fail("parse win10 no default rasd NS"); 34 | warn("error: $err\n"); 35 | } else { 36 | ok('parse win10 no default rasd NS'); 37 | } 38 | 39 | print "testing disks\n"; 40 | 41 | is($win2008->{disks}->[0]->{disk_address}, 'scsi0', 'multidisk vm has the correct first disk controller'); 42 | is($win2008->{disks}->[0]->{backing_file}, "$test_manifests/disk1.vmdk", 'multidisk vm has the correct first disk backing device'); 43 | is($win2008->{disks}->[0]->{virtual_size}, 2048, 'multidisk vm has the correct first disk size'); 44 | 45 | is($win2008->{disks}->[1]->{disk_address}, 'scsi1', 'multidisk vm has the correct second disk controller'); 46 | is($win2008->{disks}->[1]->{backing_file}, "$test_manifests/disk2.vmdk", 'multidisk vm has the correct second disk backing device'); 47 | is($win2008->{disks}->[1]->{virtual_size}, 2048, 'multidisk vm has the correct second disk size'); 48 | 49 | is($win10->{disks}->[0]->{disk_address}, 'scsi0', 'single disk vm has the correct disk controller'); 50 | is($win10->{disks}->[0]->{backing_file}, "$test_manifests/Win10-Liz-disk1.vmdk", 'single disk vm has the correct disk backing device'); 51 | is($win10->{disks}->[0]->{virtual_size}, 2048, 'single disk vm has the correct size'); 52 | 53 | is($win10noNs->{disks}->[0]->{disk_address}, 'scsi0', 'single disk vm (no default rasd NS) has the correct disk controller'); 54 | is($win10noNs->{disks}->[0]->{backing_file}, "$test_manifests/Win10-Liz-disk1.vmdk", 'single disk vm (no default rasd NS) has the correct disk backing device'); 55 | is($win10noNs->{disks}->[0]->{virtual_size}, 2048, 'single disk vm (no default rasd NS) has the correct size'); 56 | 57 | print "testing nics\n"; 58 | is($win2008->{net}->{net0}->{model}, 'e1000', 'win2008 has correct nic model'); 59 | is($win10->{net}->{net0}->{model}, 'e1000e', 'win10 has correct nic model'); 60 | is($win10noNs->{net}->{net0}->{model}, 'e1000e', 'win10 (no default rasd NS) has correct nic model'); 61 | 62 | print "\ntesting vm.conf extraction\n"; 63 | 64 | is($win2008->{qm}->{boot}, 'order=scsi0;scsi1', 'win2008 VM boot is correct'); 65 | is($win2008->{qm}->{name}, 'Win2008-R2x64', 'win2008 VM name is correct'); 66 | is($win2008->{qm}->{memory}, '2048', 'win2008 VM memory is correct'); 67 | is($win2008->{qm}->{cores}, '1', 'win2008 VM cores are correct'); 68 | is($win2008->{qm}->{ostype}, 'win7', 'win2008 VM ostype is correcty'); 69 | 70 | is($win10->{qm}->{boot}, 'order=scsi0', 'win10 VM boot is correct'); 71 | is($win10->{qm}->{name}, 'Win10-Liz', 'win10 VM name is correct'); 72 | is($win10->{qm}->{memory}, '6144', 'win10 VM memory is correct'); 73 | is($win10->{qm}->{cores}, '4', 'win10 VM cores are correct'); 74 | # older esxi/ovf standard used 'other' for windows10 75 | is($win10->{qm}->{ostype}, 'other', 'win10 VM ostype is correct'); 76 | 77 | is($win10noNs->{qm}->{boot}, 'order=scsi0', 'win10 VM (no default rasd NS) boot is correct'); 78 | is($win10noNs->{qm}->{name}, 'Win10-Liz', 'win10 VM (no default rasd NS) name is correct'); 79 | is($win10noNs->{qm}->{memory}, '6144', 'win10 VM (no default rasd NS) memory is correct'); 80 | is($win10noNs->{qm}->{cores}, '4', 'win10 VM (no default rasd NS) cores are correct'); 81 | # older esxi/ovf standard used 'other' for windows10 82 | is($win10noNs->{qm}->{ostype}, 'other', 'win10 VM (no default rasd NS) ostype is correct'); 83 | is($win10noNs->{qm}->{bios}, 'ovmf', 'win10 VM (no default rasd NS) bios is correct'); 84 | 85 | done_testing(); 86 | -------------------------------------------------------------------------------- /src/test/run_plugin_tests.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | # to ensure consistent ctime values on all systems 7 | $ENV{TZ} = 'UTC'; 8 | 9 | use TAP::Harness; 10 | 11 | my $harness = TAP::Harness->new( { verbosity => -1 }); 12 | my $res = $harness->runtests( 13 | "archive_info_test.pm", 14 | "parse_volname_test.pm", 15 | "list_volumes_test.pm", 16 | "path_to_volume_id_test.pm", 17 | "get_subdir_test.pm", 18 | "filesystem_path_test.pm", 19 | "prune_backups_test.pm", 20 | ); 21 | 22 | exit -1 if !$res || $res->{failed} || $res->{parse_errors}; 23 | -------------------------------------------------------------------------------- /src/udev-rbd/50-rbd-pve.rules: -------------------------------------------------------------------------------- 1 | KERNEL=="rbd[0-9]*", ENV{DEVTYPE}=="disk", PROGRAM="/usr/libexec/ceph-rbdnamer-pve %k", SYMLINK+="rbd-pve/%c" 2 | KERNEL=="rbd[0-9]*", ENV{DEVTYPE}=="partition", PROGRAM="/usr/libexec/ceph-rbdnamer-pve %k", SYMLINK+="rbd-pve/%c-part%n" 3 | -------------------------------------------------------------------------------- /src/udev-rbd/Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE=libpve-storage-perl 2 | 3 | DESTDIR= 4 | PREFIX=/usr 5 | LIBEXECDIR=${PREFIX}/libexec 6 | LIBDIR=${PREFIX}/lib 7 | 8 | all: 9 | 10 | .PHONY: install 11 | install: 50-rbd-pve.rules ceph-rbdnamer-pve 12 | install -d ${DESTDIR}${LIBEXECDIR} 13 | install -m 0755 ceph-rbdnamer-pve ${DESTDIR}${LIBEXECDIR} 14 | install -d ${DESTDIR}${LIBDIR}/udev/rules.d 15 | install -m 0644 50-rbd-pve.rules ${DESTDIR}${LIBDIR}/udev/rules.d 16 | 17 | .PHONY: clean 18 | clean: 19 | 20 | .PHONY: distclean 21 | distclean: clean 22 | -------------------------------------------------------------------------------- /src/udev-rbd/ceph-rbdnamer-pve: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DEV=$1 4 | NUM=`echo $DEV | sed 's#p.*##g; s#[a-z]##g'` 5 | POOL=`cat /sys/devices/rbd/$NUM/pool` 6 | CLUSTER_FSID=`cat /sys/devices/rbd/$NUM/cluster_fsid` 7 | 8 | if [ -f /sys/devices/rbd/$NUM/pool_ns ]; then 9 | NAMESPACE=`cat /sys/devices/rbd/$NUM/pool_ns` 10 | else 11 | NAMESPACE="" 12 | fi 13 | IMAGE=`cat /sys/devices/rbd/$NUM/name` 14 | SNAP=`cat /sys/devices/rbd/$NUM/current_snap` 15 | 16 | echo -n "/$CLUSTER_FSID/$POOL" 17 | 18 | if [ -n "$NAMESPACE" ]; then 19 | echo -n "/$NAMESPACE" 20 | fi 21 | echo -n "/$IMAGE" 22 | if [ "$SNAP" != "-" ]; then 23 | echo -n "@$SNAP" 24 | fi 25 | --------------------------------------------------------------------------------