[system-storage-manager/f19] Update to a new upstream release v0.4 and more fixes
Lukas Czerner
lczerner at fedoraproject.org
Mon Jan 20 16:42:30 UTC 2014
commit 5d844463f56b31f25acc1ceb22c135e84334b1e7
Author: Lukas Czerner <lczerner at redhat.com>
Date: Mon Jan 20 14:35:57 2014 +0100
Update to a new upstream release v0.4 and more fixes
- Remove btrfs resize support
- Unmount all btrfs subvolumes when removing a filesystem
- Fix size argument parsing for create and snapshot command
- Fix list output for some cases
- Add support to create encrypted volumes with crypt backend
- Add dry-run option
- Fix removing volumes with crypt backend
- Add raid1 and raid10 support for lvm backend
- Allow to check btrfs volumes
- Fix error handling when trying to resize btrfs subvolume
- Fix ssm mount command so it detects directory properly
- Suppress backtrace when a command fails
- Fix ssm to recognize units in new btrfs output properly
- Use correct sysfs file to get size for a partition
- Fix ssm to be able add a device with signature to btrfs file system
- Resognize btrfs devices from new btrfs output properly
Signed-off-by: Lukas Czerner <lczerner at redhat.com>
.gitignore | 1 +
...-new-upstream-release-v0.4-and-more-fixes.patch | 4652 ++++++++++++++++++++
sources | 2 +-
...t_device_size-to-work-with-partitions-cor.patch | 39 +
...4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch | 103 +
...t_unit_size-accept-different-unit-formats.patch | 221 +
...-SSM_PRINT_BACKTRACE-environment-variable.patch | 57 +
...-ssm-Fix-various-problems-found-by-pylint.patch | 222 +
...ssm-Remove-unnecessary-usr-bin-env-python.patch | 124 +
...-ssm-Suppress-backtrace-if-command-failed.patch | 39 +
ssm-0.4-ssm-big-update-no-1.patch | 3126 +++++++++++++
...btrfs-backend-try-to-find-the-real-device.patch | 31 +
ssm-0.4-ssm-force-btrfs-device-add.patch | 341 ++
...vice-count-should-go-down-after-removing-.patch | 27 +
...hey-way-we-gather-information-about-btrfs.patch | 27 +
...btrfs-recognize-devices-from-tests-suite-.patch | 38 +
system-storage-manager.spec | 69 +-
17 files changed, 9116 insertions(+), 3 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 927b346..4ac3cf3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
/system-storage-manager-0.2.tar.gz
+/system-storage-manager-0.4.tar.gz
diff --git a/0001-Update-to-a-new-upstream-release-v0.4-and-more-fixes.patch b/0001-Update-to-a-new-upstream-release-v0.4-and-more-fixes.patch
new file mode 100644
index 0000000..55271e2
--- /dev/null
+++ b/0001-Update-to-a-new-upstream-release-v0.4-and-more-fixes.patch
@@ -0,0 +1,4652 @@
+From 82aefe2b0f9332b6ad60958377216a721e01b452 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Mon, 20 Jan 2014 14:35:57 +0100
+Subject: [PATCH] Update to a new upstream release v0.4 and more fixes
+
+- Remove btrfs resize support
+- Unmount all btrfs subvolumes when removing a filesystem
+- Fix size argument parsing for create and snapshot command
+- Fix list output for some cases
+- Add support to create encrypted volumes with crypt backend
+- Add dry-run option
+- Fix removing volumes with crypt backend
+- Add raid1 and raid10 support for lvm backend
+- Allow to check btrfs volumes
+- Fix error handling when trying to resize btrfs subvolume
+- Fix ssm mount command so it detects directory properly
+- Suppress backtrace when a command fails
+- Fix ssm to recognize units in new btrfs output properly
+- Use correct sysfs file to get size for a partition
+- Fix ssm to be able add a device with signature to btrfs file system
+- Resognize btrfs devices from new btrfs output properly
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ .gitignore | 1 +
+ sources | 2 +-
+ ...t_device_size-to-work-with-partitions-cor.patch | 39 +
+ ...4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch | 103 +
+ ...t_unit_size-accept-different-unit-formats.patch | 221 ++
+ ...-SSM_PRINT_BACKTRACE-environment-variable.patch | 57 +
+ ...-ssm-Fix-various-problems-found-by-pylint.patch | 222 ++
+ ...ssm-Remove-unnecessary-usr-bin-env-python.patch | 124 +
+ ...-ssm-Suppress-backtrace-if-command-failed.patch | 39 +
+ ssm-0.4-ssm-big-update-no-1.patch | 3126 ++++++++++++++++++++
+ ...btrfs-backend-try-to-find-the-real-device.patch | 31 +
+ ssm-0.4-ssm-force-btrfs-device-add.patch | 341 +++
+ ...vice-count-should-go-down-after-removing-.patch | 27 +
+ ...hey-way-we-gather-information-about-btrfs.patch | 27 +
+ ...btrfs-recognize-devices-from-tests-suite-.patch | 38 +
+ system-storage-manager.spec | 68 +-
+ 16 files changed, 4464 insertions(+), 2 deletions(-)
+ create mode 100644 ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch
+ create mode 100644 ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch
+ create mode 100644 ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch
+ create mode 100644 ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch
+ create mode 100644 ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch
+ create mode 100644 ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch
+ create mode 100644 ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch
+ create mode 100644 ssm-0.4-ssm-big-update-no-1.patch
+ create mode 100644 ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch
+ create mode 100644 ssm-0.4-ssm-force-btrfs-device-add.patch
+ create mode 100644 ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch
+ create mode 100644 ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch
+ create mode 100644 ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch
+
+diff --git a/.gitignore b/.gitignore
+index 927b346..4ac3cf3 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -1 +1,2 @@
+ /system-storage-manager-0.2.tar.gz
++/system-storage-manager-0.4.tar.gz
+diff --git a/sources b/sources
+index 038df21..f176aa7 100644
+--- a/sources
++++ b/sources
+@@ -1 +1 @@
+-524654f3b7bab544c4e612470b5d2392 system-storage-manager-0.2.tar.gz
++43235208daf95f8ada1b0632bf7bc756 system-storage-manager-0.4.tar.gz
+diff --git a/ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch b/ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch
+new file mode 100644
+index 0000000..3aac93c
+--- /dev/null
++++ b/ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch
+@@ -0,0 +1,39 @@
++From 5b917ffbddfbb7366d2ad7d5f0e9c016ced29ca5 Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Wed, 15 Jan 2014 17:52:28 +0100
++Subject: [PATCH 07/10] misc: Fix get_device_size to work with partitions
++ correctly
++
++Currently get_device_size() searches for the size in sysfs specifically
++(/sys/block/<devname>/size) however that is not the right palce for
++partitions since partitions are subdirectories in /sys/block/<devname>
++directory.
++
++So change get_device_size() to get major and minor numbers of the device
++in question and use sysfs file /sys/dev/block/<major>:<minor>/size to
++get the size.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/misc.py | 5 +++--
++ 1 file changed, 3 insertions(+), 2 deletions(-)
++
++diff --git a/ssmlib/misc.py b/ssmlib/misc.py
++index bfaa1c4..dbfa6b1 100644
++--- a/ssmlib/misc.py
+++++ b/ssmlib/misc.py
++@@ -608,8 +608,9 @@ def is_bdevice(path):
++
++
++ def get_device_size(device):
++- devname = device.split('/')[-1]
++- with open("/sys/block/{0}/size".format(devname), 'r') as f:
+++ info = os.stat(device)
+++ major, minor = divmod(info.st_rdev, 256)
+++ with open("/sys/dev/block/{0}:{1}/size".format(major, minor), 'r') as f:
++ for line in f:
++ size = int(line)/2
++ return size
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch b/ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch
+new file mode 100644
+index 0000000..9a11955
+--- /dev/null
++++ b/ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch
+@@ -0,0 +1,103 @@
++From 37b31373b1408c299270e3134b88eff4528ca75a Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Thu, 16 Jan 2014 17:31:57 +0100
++Subject: [PATCH 10/10] misc: Use wipefs -a in misc.wipefs() helper
++
++Currently we only wipe the first signature we can find with wipefs.
++However wipefs has an ability to wipe all the signatures of given type
++from the device (yes this is once again about btrfs) with option '-a'.
++So use that instead. This also simplify the wipefs helper a lot.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/misc.py | 14 ++------------
++ tests/unittests/test_btrfs.py | 34 +++++++++++++++++-----------------
++ 2 files changed, 19 insertions(+), 29 deletions(-)
++
++diff --git a/ssmlib/misc.py b/ssmlib/misc.py
++index 3d99c07..915cad1 100644
++--- a/ssmlib/misc.py
+++++ b/ssmlib/misc.py
++@@ -363,18 +363,8 @@ def get_dmnumber(name):
++ def wipefs(device, signatures):
++ if type(signatures) is not list:
++ signatures = [signatures]
++- command = ['wipefs', '-p', device]
++- output = run(command)[1]
++- offset = []
++- for line in output[1:].split('\n'):
++- if not line:
++- continue
++- array = line.split(",")
++- if array[-1] in signatures:
++- offset.extend(['--offset', array[0]])
++- if len(offset) > 1:
++- command = ['wipefs'] + offset + [device]
++- run(command)
+++ command = ['wipefs', '-a', '-t', ','.join(signatures), device]
+++ run(command)
++
++
++ def humanize_size(arg):
++diff --git a/tests/unittests/test_btrfs.py b/tests/unittests/test_btrfs.py
++index 2fb04bc..2b6cd8e 100644
++--- a/tests/unittests/test_btrfs.py
+++++ b/tests/unittests/test_btrfs.py
++@@ -167,12 +167,12 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ self._addPool('my_pool', ['/dev/sdc2', '/dev/sdc3', '/dev/sdc1'])
++
++ # remove volume
++- self._checkCmd("ssm remove default_pool", [], "wipefs -p /dev/sda")
++- self._cmdEq("wipefs -p /dev/sdb", -2)
+++ self._checkCmd("ssm remove default_pool", [], "wipefs -a -t btrfs /dev/sda")
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdb", -2)
++
++- self._checkCmd("ssm remove my_pool", [], "wipefs -p /dev/sdc3")
++- self._cmdEq("wipefs -p /dev/sdc1", -2)
++- self._cmdEq("wipefs -p /dev/sdc2", -3)
+++ self._checkCmd("ssm remove my_pool", [], "wipefs -a -t btrfs /dev/sdc3")
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdc1", -2)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdc2", -3)
++
++ # remove subvolume
++ self._addVol('vol001', 117283225, 1, 'default_pool', ['/dev/sda'], '/mnt/test')
++@@ -189,24 +189,24 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ self._addPool('other_pool', ['/dev/sdd', '/dev/sde'])
++ self._checkCmd("ssm remove /dev/sdd /dev/sdb other_pool my_pool default_pool:/dev/default_pool/vol001", [],
++ "btrfs subvolume delete /mnt/test")
++- self._cmdEq("wipefs -p /dev/sdc1", -2)
++- self._cmdEq("wipefs -p /dev/sdc3", -3)
++- self._cmdEq("wipefs -p /dev/sdc2", -4)
++- self._cmdEq("wipefs -p /dev/sde", -5)
++- self._cmdEq("wipefs -p /dev/sdd", -6)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdc1", -2)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdc3", -3)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdc2", -4)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sde", -5)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdd", -6)
++ self._cmdEq("btrfs device delete /dev/sdb /mnt/test", -7)
++ self._cmdEq("btrfs device delete /dev/sdd /tmp/mount", -8)
++
++ self._removeMount("/dev/sda")
++ # remove all
++ self._checkCmd("ssm remove --all", [],
++- "wipefs -p /dev/sde")
++- self._cmdEq("wipefs -p /dev/sdd", -2)
++- self._cmdEq("wipefs -p /dev/sdc1", -3)
++- self._cmdEq("wipefs -p /dev/sdc3", -4)
++- self._cmdEq("wipefs -p /dev/sdc2", -5)
++- self._cmdEq("wipefs -p /dev/sda", -6)
++- self._cmdEq("wipefs -p /dev/sdb", -7)
+++ "wipefs -a -t btrfs /dev/sde")
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdd", -2)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdc1", -3)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdc3", -4)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdc2", -5)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sda", -6)
+++ self._cmdEq("wipefs -a -t btrfs /dev/sdb", -7)
++
++ # TODO
++ # remove force
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch b/ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch
+new file mode 100644
+index 0000000..e74536a
+--- /dev/null
++++ b/ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch
+@@ -0,0 +1,221 @@
++From ef26cf6fb0e33ca52f35fcf219fd21db7788c200 Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Mon, 13 Jan 2014 16:29:01 +0100
++Subject: [PATCH 01/10] misc: get_unit_size() accept different unit formats
++
++Because btrfs recently changed it's output to use MiB format instead of
++MB format the function handling this within the btrfs is broken.
++
++Fix this my making get_unit_size() to handle different unit formats (M,
++MB, MiB) and use that in btrfs as well.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/backends/btrfs.py | 18 ++---------
++ ssmlib/misc.py | 82 ++++++++++++++++++++++++++++++++++--------------
++ test.py | 13 ++++----
++ 3 files changed, 69 insertions(+), 44 deletions(-)
++
++diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
++index a4aedda..eb23403 100644
++--- a/ssmlib/backends/btrfs.py
+++++ b/ssmlib/backends/btrfs.py
++@@ -33,18 +33,6 @@ except KeyError:
++ SSM_BTRFS_DEFAULT_POOL = "btrfs_pool"
++
++
++-def get_real_number(string):
++- number = float(string[0:-2])
++- unit = string[-2:-1]
++- # The result will be in kilobytes
++- units = ["K", "M", "G", "T", "P", "E", "Z", "Y"]
++- for i, u in enumerate(units):
++- if u == unit:
++- number *= (2 ** (i * 10))
++- break
++- return number
++-
++-
++ def get_btrfs_version():
++ try:
++ output = misc.run(['btrfs', '--version'], can_fail=True)[1]
++@@ -128,7 +116,7 @@ class Btrfs(template.Backend):
++
++ elif array[0] == 'Total':
++ pool['dev_count'] = array[2]
++- fs_used = get_real_number(array[6])
+++ fs_used = float(misc.get_real_size(array[6]))
++
++ elif array[0] == 'devid':
++ dev['dev_name'] = array[7]
++@@ -154,9 +142,9 @@ class Btrfs(template.Backend):
++ vol['real_dev'] = found[0].split(':')[0]
++ break
++
++- dev_used = get_real_number(array[5])
+++ dev_used = float(misc.get_real_size(array[5]))
++ dev['dev_used'] = str(dev_used)
++- fs_size += get_real_number(array[3])
+++ fs_size += float(misc.get_real_size(array[3]))
++
++ dev_size = \
++ int(partitions[dev['dev_name'].rpartition("/")[-1]][2])
++diff --git a/ssmlib/misc.py b/ssmlib/misc.py
++index 3ff1577..bfaa1c4 100644
++--- a/ssmlib/misc.py
+++++ b/ssmlib/misc.py
++@@ -32,34 +32,57 @@ TMP_MOUNTED = []
++
++ def get_unit_size(string):
++ """
++- Check the last character of the string for the unit and return the unit
++- value, otherwise return zero. It check only the last character of the
++- string.
+++ Check the last character of the string for the unit and return the tuple
+++ of unit value, unit name, otherwise return zero. It check only the first
+++ character of the unit string.
++
++ >>> get_unit_size("B")
++- 1
+++ (1, 'B')
++ >>> get_unit_size("k")
++- 1024
++- >>> get_unit_size("M")
++- 1048576
++- >>> get_unit_size("g")
++- 1073741824
++- >>> get_unit_size("T")
++- 1099511627776...
++- >>> get_unit_size("p")
++- 1125899906842624...
+++ (1024, 'k')
+++ >>> get_unit_size("KiB")
+++ (1024, 'KiB')
+++ >>> get_unit_size("KB")
+++ (1024, 'KB')
+++ >>> get_unit_size("10M")
+++ (1048576, 'M')
+++ >>> get_unit_size("-99.99g")
+++ (1073741824, 'g')
+++ >>> get_unit_size("-99.99GiB")
+++ (1073741824, 'GiB')
+++ >>> get_unit_size("+99.99G")
+++ (1073741824, 'G')
+++ >>> get_unit_size("+99.99gb")
+++ (1073741824, 'gb')
+++ >>> get_unit_size("99.99g")
+++ (1073741824, 'g')
+++ >>> get_unit_size("0.84T")
+++ (1099511627776, 'T')
+++ >>> get_unit_size("0.84TiB")
+++ (1099511627776, 'TiB')
+++ >>> get_unit_size("0p")
+++ (1125899906842624, 'p')
+++ >>> get_unit_size("99kit")
+++ (0, '')
++ >>> get_unit_size("")
++- 0
+++ (0, '')
++ >>> get_unit_size("H")
++- 0
+++ (0, '')
++ """
++
++ mult = 0
++ units = {'B': 1, 'K': 2 ** 10, 'M': 2 ** 20, 'G': 2 ** 30, 'T': 2 ** 40,
++ 'P': 2 ** 50}
++- if len(string) > 0 and string[-1].upper() in units:
++- mult = units[string[-1].upper()]
++- return mult
+++ unit = re.sub(r'^\+?-?\d+(\.\d*)?', '', string)
+++ if len(unit) > 0 and unit[0].upper() in units:
+++ mult = units[unit[0].upper()]
+++ all_units = {'B', 'K', 'M', 'G', 'T', 'P',
+++ 'KB', 'MB', 'GB', 'TB', 'PB',
+++ 'KIB', 'MIB', 'GIB', 'TIB', 'PIB'}
+++ if unit.upper() in all_units:
+++ return mult, unit
+++ else:
+++ return 0, ""
++
++
++ def is_number(string):
++@@ -92,8 +115,14 @@ def get_real_size(size):
++
++ >>> get_real_size("3141")
++ '3141'
+++ >>> get_real_size("3141B")
+++ '3.07'
++ >>> get_real_size("3141K")
++ '3141.00'
+++ >>> get_real_size("3141KB")
+++ '3141.00'
+++ >>> get_real_size("3141KiB")
+++ '3141.00'
++ >>> get_real_size("3141k")
++ '3141.00'
++ >>> get_real_size("3141M")
++@@ -118,6 +147,10 @@ def get_real_size(size):
++ '-3.14'
++ >>> get_real_size("3.14G")
++ '3292528.64'
+++ >>> get_real_size("3.14GB")
+++ '3292528.64'
+++ >>> get_real_size("3.14GiB")
+++ '3292528.64'
++ >>> get_real_size("+3.14g")
++ '+3292528.64'
++ >>> get_real_size("-3.14G")
++@@ -133,12 +166,15 @@ def get_real_size(size):
++ """
++ if is_number(size):
++ return size
++- elif is_number(size[:-1]):
+++ else:
++ # Always use kilobytes in ssm
++- mult = get_unit_size(size) / 1024
++- sign = '+' if size[0] == '+' else ''
++- if mult:
++- return '{0}{1:.2f}'.format(sign, float(size[:-1]) * mult)
+++ mult, unit = get_unit_size(size)
+++ mult /= float(1024)
+++ number = re.sub(unit + "$", '', size)
+++ if is_number(number):
+++ sign = '+' if size[0] == '+' else ''
+++ if mult:
+++ return '{0}{1:.2f}'.format(sign, float(number) * mult)
++ raise Exception("Not supported unit in the " +
++ "size \'{0}\' argument.".format(size))
++
++diff --git a/test.py b/test.py
++index 2ca542c..7c91ae8 100644
++--- a/test.py
+++++ b/test.py
++@@ -89,17 +89,18 @@ def run_bash_tests():
++
++ def quick_test():
++ print "[+] Running doctests"
++- doctest_flags = doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS
+++ doctest_flags = doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS | \
+++ doctest.REPORT_ONLY_FIRST_FAILURE
++ result = doctest.testmod(main, exclude_empty=True, report=True,
++- raise_on_error=True, optionflags=doctest_flags)
+++ raise_on_error=False, optionflags=doctest_flags)
++ result = doctest.testmod(lvm, exclude_empty=True, report=True,
++- raise_on_error=True, optionflags=doctest_flags)
+++ raise_on_error=False, optionflags=doctest_flags)
++ result = doctest.testmod(crypt, exclude_empty=True, report=True,
++- raise_on_error=True, optionflags=doctest_flags)
+++ raise_on_error=False, optionflags=doctest_flags)
++ result = doctest.testmod(btrfs, exclude_empty=True, report=True,
++- raise_on_error=True, optionflags=doctest_flags)
+++ raise_on_error=False, optionflags=doctest_flags)
++ result = doctest.testmod(misc, exclude_empty=True, report=True,
++- raise_on_error=True, optionflags=doctest_flags)
+++ raise_on_error=False, optionflags=doctest_flags)
++ print "[+] Running unittests"
++ test_loader = unittest.TestLoader()
++ tests_lvm = test_loader.loadTestsFromModule(test_lvm)
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch b/ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch
+new file mode 100644
+index 0000000..e305100
+--- /dev/null
++++ b/ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch
+@@ -0,0 +1,57 @@
++From e5577724d859ca19e6b3074f05f25bc43c748a43 Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Thu, 16 Jan 2014 15:08:11 +0100
++Subject: [PATCH 09/10] ssm: Add SSM_PRINT_BACKTRACE environment variable
++
++Currently we're catching all the SsmError exception and just print out
++simple error message. This is fine, however it makes debugging harder in
++some cases, so add SSM_PRINT_BACKTRACE environment variable which
++enables us to print traceback when needed. Simply set the variable to
++yes, true, or 1 and ssm will print traceback in case of error.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ bin/ssm | 20 +++++++++++++++++++-
++ 1 file changed, 19 insertions(+), 1 deletion(-)
++
++diff --git a/bin/ssm b/bin/ssm
++index baa3c60..6058f64 100755
++--- a/bin/ssm
+++++ b/bin/ssm
++@@ -19,8 +19,23 @@
++
++ import os
++ import sys
+++import traceback
++ from ssmlib import problem
++
+++
+++# Should we print backtrace on error, or suppress it ?
+++# Suppress it by default.
+++try:
+++ SSM_PRINT_BACKTRACE = os.environ['SSM_PRINT_BACKTRACE']
+++ if SSM_PRINT_BACKTRACE.upper() in ['YES', 'TRUE', '1']:
+++ SSM_PRINT_BACKTRACE = True
+++ else:
+++ SSM_PRINT_BACKTRACE = False
+++except KeyError:
+++ SSM_PRINT_BACKTRACE = False
+++
+++
+++
++ try:
++ from ssmlib import main
++
++@@ -32,5 +47,8 @@ try:
++ sys.exit("\nRoot privileges required to run this script!\n")
++ sys.exit(main.main())
++ except problem.SsmError, err:
++- print str(err)
+++ if SSM_PRINT_BACKTRACE is True:
+++ traceback.print_exc(file=sys.stdout)
+++ else:
+++ print str(err)
++ sys.exit(err.errcode)
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch b/ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch
+new file mode 100644
+index 0000000..f436c38
+--- /dev/null
++++ b/ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch
+@@ -0,0 +1,222 @@
++From 0dcf4bcee5c5f6503905dac298dee77f1141f783 Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Fri, 17 Jan 2014 16:05:56 +0100
++Subject: [PATCH] ssm: Fix various problems found by pylint
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/__init__.py | 2 +-
++ ssmlib/backends/btrfs.py | 12 +++++-------
++ ssmlib/backends/crypt.py | 5 ++---
++ ssmlib/backends/lvm.py | 3 +--
++ ssmlib/backends/md.py | 3 ---
++ ssmlib/main.py | 6 +++---
++ ssmlib/misc.py | 12 ++++++------
++ 7 files changed, 18 insertions(+), 25 deletions(-)
++
++diff --git a/ssmlib/__init__.py b/ssmlib/__init__.py
++index f1bcfdb..0a49de2 100644
++--- a/ssmlib/__init__.py
+++++ b/ssmlib/__init__.py
++@@ -15,4 +15,4 @@
++ # You should have received a copy of the GNU General Public License
++ # along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++-__all__ = ["backends", "misc", "main", "problem"]
+++__all__ = ["backend", "misc", "main", "problem"]
++diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
++index 836f108..acd5446 100644
++--- a/ssmlib/backends/btrfs.py
+++++ b/ssmlib/backends/btrfs.py
++@@ -19,10 +19,8 @@
++
++ import re
++ import os
++-import sys
++ import datetime
++ from ssmlib import misc
++-from ssmlib import problem
++ from ssmlib.backends import template
++
++ __all__ = ["BtrfsVolume", "BtrfsPool", "BtrfsDev"]
++@@ -37,7 +35,7 @@ def get_btrfs_version():
++ try:
++ output = misc.run(['btrfs', '--version'], can_fail=True)[1]
++ output = output.strip().split("\n")[-1]
++- version = re.search('(?<=v)\d+\.\d+', output).group(0)
+++ version = re.search(r'(?<=v)\d+\.\d+', output).group(0)
++ except (OSError, AttributeError):
++ version = "0.0"
++ return float(version)
++@@ -262,7 +260,7 @@ class Btrfs(template.Backend):
++ new['hide'] = False
++ # Store snapshot info
++ if 'mount' in new and \
++- re.match("snap-\d{4}-\d{2}-\d{2}-T\d{6}",
+++ re.match(r"snap-\d{4}-\d{2}-\d{2}-T\d{6}",
++ os.path.basename(new['mount'])):
++ new['snap_name'] = "{0}:{1}".format(name,
++ os.path.basename(new['path']))
++@@ -279,8 +277,8 @@ class Btrfs(template.Backend):
++ continue
++ # For the version with screwed 'subvolume list' command
++ line = re.sub("<FS_TREE>/*", "", line)
++- volume['ID'] = re.search('(?<=ID )\d+', line).group(0)
++- volume['top_level'] = re.search('(?<=top level )\d+', line).group(0)
+++ volume['ID'] = re.search(r'(?<=ID )\d+', line).group(0)
+++ volume['top_level'] = re.search(r'(?<=top level )\d+', line).group(0)
++ volume['path'] = re.search('(?<=path ).*$', line).group(0)
++ volume['subvolume'] = True
++ yield volume
++@@ -450,7 +448,7 @@ class BtrfsPool(Btrfs, template.BackendPool):
++ command.extend(['-m', 'raid10', '-d', 'raid10'])
++ else:
++ raise Exception("Btrfs backed currently does not support " +
++- "RAID level {0}".format(raid['level']))
+++ "RAID level {0}".format(options['raid']))
++
++ if size:
++ command.extend(['-b', "{0}".format(int(float(size) * 1024))])
++diff --git a/ssmlib/backends/crypt.py b/ssmlib/backends/crypt.py
++index 4aad266..8c1b48c 100644
++--- a/ssmlib/backends/crypt.py
+++++ b/ssmlib/backends/crypt.py
++@@ -21,7 +21,6 @@ import re
++ import os
++ import stat
++ from ssmlib import misc
++-from ssmlib import problem
++ from ssmlib.backends import template
++
++ __all__ = ["DmCryptVolume"]
++@@ -240,7 +239,7 @@ class DmCryptDevice(DmObject, template.BackendDevice):
++ device = {}
++ devname = "/dev/" + line[3]
++ signature = misc.get_signature(devname)
++- if misc.get_signature(devname) in CRYPT_SIGNATURES:
+++ if signature in CRYPT_SIGNATURES:
++ device['hide'] = False
++ device['dev_name'] = devname
++ device['pool_name'] = self.default_pool_name
++@@ -250,4 +249,4 @@ class DmCryptDevice(DmObject, template.BackendDevice):
++
++
++ def remove(self, devices):
++- misc.wipefs(device, CRYPT_SIGNATURES)
+++ misc.wipefs(devices, CRYPT_SIGNATURES)
++diff --git a/ssmlib/backends/lvm.py b/ssmlib/backends/lvm.py
++index 955c705..4c7c0ad 100644
++--- a/ssmlib/backends/lvm.py
+++++ b/ssmlib/backends/lvm.py
++@@ -22,7 +22,6 @@ import os
++ import stat
++ import datetime
++ from ssmlib import misc
++-from ssmlib import problem
++ from ssmlib.backends import template
++
++ __all__ = ["PvsInfo", "VgsInfo", "LvsInfo"]
++@@ -47,7 +46,7 @@ def get_lvm_version():
++ for line in output:
++ if pattern.match(line.strip()):
++ match = " ".join(line.split())
++- tmp = re.search('(?<=LVM version: )\d+\.\d+\.\d+',
+++ tmp = re.search(r'(?<=LVM version: )\d+\.\d+\.\d+',
++ match).group(0)
++ version = map(int, tmp.split(".", 3))
++ except (OSError, AttributeError):
++diff --git a/ssmlib/backends/md.py b/ssmlib/backends/md.py
++index f33243c..1e3c6e2 100644
++--- a/ssmlib/backends/md.py
+++++ b/ssmlib/backends/md.py
++@@ -18,11 +18,8 @@
++ # md module for System Storage Manager
++
++ import os
++-import stat
++ import socket
++-import datetime
++ from ssmlib import misc
++-from ssmlib import problem
++ from ssmlib.backends import template
++
++ try:
++diff --git a/ssmlib/main.py b/ssmlib/main.py
++index 7aab312..c294aec 100644
++--- a/ssmlib/main.py
+++++ b/ssmlib/main.py
++@@ -1284,7 +1284,7 @@ class StorageHandle(object):
++ try:
++ pool.remove()
++ removed += 1
++- except (RuntimeError, problem.SsmError), ex:
+++ except (RuntimeError, problem.SsmError):
++ PR.info("Unable to remove '{0}'".format(pool['pool_name']))
++ ret = False
++ if removed == 0:
++@@ -1378,7 +1378,6 @@ class StorageHandle(object):
++ dev = self.dev[real]
++ if dev and 'fs_info' in dev:
++ return dev
++- err = "'{0}' does not contain valid file system".format(real)
++ return False
++
++ def _find_device_record(self, path):
++@@ -1544,6 +1543,7 @@ def valid_resize_size(size):
++
++ def is_directory(string):
++ if string is None:
+++ err = "Directory name not defined."
++ raise argparse.ArgumentTypeError(err)
++ try:
++ mode = os.stat(string).st_mode
++@@ -1847,7 +1847,7 @@ def main(args=None):
++ sys.exitfunc = misc.do_cleanup
++
++ if args.dry_run:
++- return 0;
+++ return 0
++
++ try:
++ args.func(args)
++diff --git a/ssmlib/misc.py b/ssmlib/misc.py
++index 915cad1..77ddc2d 100644
++--- a/ssmlib/misc.py
+++++ b/ssmlib/misc.py
++@@ -77,9 +77,9 @@ def get_unit_size(string):
++ unit = re.sub(r'^\+?-?\d+(\.\d*)?', '', string)
++ if len(unit) > 0 and unit[0].upper() in units:
++ mult = units[unit[0].upper()]
++- all_units = {'B', 'K', 'M', 'G', 'T', 'P',
+++ all_units = ['B', 'K', 'M', 'G', 'T', 'P',
++ 'KB', 'MB', 'GB', 'TB', 'PB',
++- 'KIB', 'MIB', 'GIB', 'TIB', 'PIB'}
+++ 'KIB', 'MIB', 'GIB', 'TIB', 'PIB']
++ if unit.upper() in all_units:
++ return mult, unit
++ else:
++@@ -198,9 +198,9 @@ def get_device_by_uuid(uuid):
++
++ def get_major_minor(device):
++ real_dev = get_real_device(device)
++- stat = os.stat(real_dev)
++- major = os.major(stat.st_rdev)
++- minor = os.minor(stat.st_rdev)
+++ info = os.stat(real_dev)
+++ major = os.major(info.st_rdev)
+++ minor = os.minor(info.st_rdev)
++ return major, minor
++
++
++@@ -579,7 +579,7 @@ def terminal_size(default=(25, 80)):
++ except:
++ pass
++ if not cr:
++- cr = (25, 80)
+++ cr = default
++ return int(cr[1]), int(cr[0])
++
++
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch b/ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch
+new file mode 100644
+index 0000000..d72922c
+--- /dev/null
++++ b/ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch
+@@ -0,0 +1,124 @@
++From 7a27177855ea2466d01dd201571f66ddea6c7cc8 Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Fri, 17 Jan 2014 16:29:50 +0100
++Subject: [PATCH] ssm: Remove unnecessary /usr/bin/env python
++
++Those are not executable script and should not contain this line.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/__init__.py | 2 --
++ ssmlib/backends/__init__.py | 2 --
++ ssmlib/backends/btrfs.py | 2 --
++ ssmlib/backends/crypt.py | 2 --
++ ssmlib/backends/lvm.py | 2 --
++ ssmlib/backends/md.py | 2 --
++ ssmlib/backends/template.py | 2 --
++ ssmlib/main.py | 2 --
++ ssmlib/misc.py | 2 --
++ ssmlib/problem.py | 2 --
++ 10 files changed, 20 deletions(-)
++
++diff --git a/ssmlib/__init__.py b/ssmlib/__init__.py
++index 0a49de2..7e5e4fd 100644
++--- a/ssmlib/__init__.py
+++++ b/ssmlib/__init__.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/backends/__init__.py b/ssmlib/backends/__init__.py
++index 5720e3d..2046e83 100644
++--- a/ssmlib/backends/__init__.py
+++++ b/ssmlib/backends/__init__.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
++index acd5446..b0a486f 100644
++--- a/ssmlib/backends/btrfs.py
+++++ b/ssmlib/backends/btrfs.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/backends/crypt.py b/ssmlib/backends/crypt.py
++index 8c1b48c..642b905 100644
++--- a/ssmlib/backends/crypt.py
+++++ b/ssmlib/backends/crypt.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/backends/lvm.py b/ssmlib/backends/lvm.py
++index 4c7c0ad..4022034 100644
++--- a/ssmlib/backends/lvm.py
+++++ b/ssmlib/backends/lvm.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/backends/md.py b/ssmlib/backends/md.py
++index 1e3c6e2..fca2c98 100644
++--- a/ssmlib/backends/md.py
+++++ b/ssmlib/backends/md.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2012 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/backends/template.py b/ssmlib/backends/template.py
++index 94f4b40..bf1b6ad 100644
++--- a/ssmlib/backends/template.py
+++++ b/ssmlib/backends/template.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2013 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/main.py b/ssmlib/main.py
++index c294aec..5213000 100644
++--- a/ssmlib/main.py
+++++ b/ssmlib/main.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/misc.py b/ssmlib/misc.py
++index 77ddc2d..24b7dd9 100644
++--- a/ssmlib/misc.py
+++++ b/ssmlib/misc.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++diff --git a/ssmlib/problem.py b/ssmlib/problem.py
++index dfa6ec6..f8fb4ce 100644
++--- a/ssmlib/problem.py
+++++ b/ssmlib/problem.py
++@@ -1,5 +1,3 @@
++-#!/usr/bin/env python
++-#
++ # (C)2012 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch b/ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch
+new file mode 100644
+index 0000000..9938a47
+--- /dev/null
++++ b/ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch
+@@ -0,0 +1,39 @@
++From e2c869393f9c1c0f1c84e1a817f411a2056d820e Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Thu, 16 Jan 2014 14:44:10 +0100
++Subject: [PATCH 08/10] ssm: Suppress backtrace if command failed
++
++Currently when command failed in misc.run(), RuntimeError() exception is
++raised and backtrace is printed to the output. However this is not
++desirable, so use problem.CommandFailed() exception which allows us to
++catch it and print out reasonable error message.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/misc.py | 3 ++-
++ 1 file changed, 2 insertions(+), 1 deletion(-)
++
++diff --git a/ssmlib/misc.py b/ssmlib/misc.py
++index dbfa6b1..3d99c07 100644
++--- a/ssmlib/misc.py
+++++ b/ssmlib/misc.py
++@@ -24,6 +24,7 @@ import stat
++ import tempfile
++ import threading
++ import subprocess
+++from ssmlib import problem
++
++ # List of temporary mount points which should be cleaned up
++ # before exiting
++@@ -480,7 +481,7 @@ def run(cmd, show_cmd=False, stdout=False, stderr=True, can_fail=False,
++ print output
++ if error is not None:
++ print error
++- raise RuntimeError(err_msg)
+++ raise problem.CommandFailed(err_msg)
++
++ if not return_stdout:
++ output = None
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-ssm-big-update-no-1.patch b/ssm-0.4-ssm-big-update-no-1.patch
+new file mode 100644
+index 0000000..11b98f3
+--- /dev/null
++++ b/ssm-0.4-ssm-big-update-no-1.patch
+@@ -0,0 +1,3126 @@
++diff --git a/Makefile b/Makefile
++index 629a83f..2d887c4 100644
++--- a/Makefile
+++++ b/Makefile
++@@ -1,3 +1,6 @@
+++PYTHONPATH := $(shell pwd)
+++export PYTHONPATH
+++
++ all: help
++
++ help:
++@@ -53,4 +56,7 @@ source: test clean
++ test:
++ @python test.py
++
+++push_html:
+++ scp -r doc/_build/singlehtml/* lczerner at shell.sourceforge.net:/home/project-web/storagemanager/htdocs/
+++
++ release: git-clean clean check_vars authors spec log docs source
++diff --git a/README b/README
++index ea3d3c3..71b09e8 100644
++--- a/README
+++++ b/README
++@@ -162,6 +162,9 @@ In some cases file system has to be mounted in order to resize. This
++ will be handled by **ssm** automatically by mounting the **volume**
++ temporarily.
++
+++Note that resizing btrfs subvolume is not supported, only the whole
+++file system can be resized.
+++
++
++ Check command
++ *************
++@@ -256,8 +259,8 @@ volume
++ Volume in btrfs back-end is actually just btrfs subvolume with the
++ exception of the first volume created on btrfs pool creation, which
++ is the file system itself. Subvolumes can only be created on btrfs
++- file system when the it is mounted, but user does not have to worry
++- about that, since **ssm** will automatically mount the file system
+++ file system when it is mounted, but user does not have to worry
+++ about that since **ssm** will automatically mount the file system
++ temporarily in order to create a new subvolume.
++
++ Volume name is used as subvolume path in the btrfs file system and
++@@ -270,6 +273,10 @@ volume
++ system is mounted, with the exception of the main btrfs volume -
++ the file system itself.
++
+++ Also note that btrfs volumes and subvolumes can not be resized.
+++ This is mainly limitation of the btrfs tools which currently does
+++ not work reliably.
+++
++ New btrfs volume can be created with **create** command.
++
++ snapshot
++@@ -323,10 +330,40 @@ device
++ Crypt backend
++ *************
++
++-Crypt backend in **ssm** is currently limited to only gather the
++-information about encrypted volumes in the system. You can not create
++-or manage encrypted volumes or pools, but it will be extended in the
++-future.
+++Crypt backend in **ssm** uses cryptsetup and dm-crypt target to manage
+++encrypted volumes. Crypt backend can be used as a regular backend for
+++creating encrypted volumes on top of regular block devices, or even
+++other volumes (lvm or md volumes for example). Or it can be used to
+++create encrypted lvm volumes right away in a single step.
+++
+++Only volumes can be created with crypt backend. This backend does not
+++support pooling and does not require special devices.
+++
+++pool
+++ Crypt backend does not support pooling it is not possible to create
+++ crypt pool or add a device into a pool.
+++
+++volume
+++ Volume in crypt backend is the volume created by dm-crypt which
+++ represent the data on the original encrypted device in unencrypted
+++ form. Crypt backend does not support pooling, so only one device
+++ can be used to create crypt volume. It also does not support raid
+++ or any device concatenation.
+++
+++ Currently two modes, or extensions are supported luks and plain.
+++ Luks is used by default.For more information about the extensions
+++ please see **cryptsetup** manual page.
+++
+++snapshot
+++ Crypt backend does not support snapshotting, however if the
+++ encrypted volume is created on top of the lvm volume, the lvm
+++ volume itself can be snapshotted. The snapshot can be then opened
+++ by using **cryptsetup**. It is possible that this might change in
+++ the future so that **ssm** will be able to activate the volume
+++ directly without the extra step.
+++
+++device
+++ Crypt backend does not require any special device to be created on.
++
++
++ Environment variables
++diff --git a/bin/ssm b/bin/ssm
++index fc1ce48..baa3c60 100755
++--- a/bin/ssm
+++++ b/bin/ssm
++@@ -32,4 +32,5 @@ try:
++ sys.exit("\nRoot privileges required to run this script!\n")
++ sys.exit(main.main())
++ except problem.SsmError, err:
+++ print str(err)
++ sys.exit(err.errcode)
++diff --git a/doc/_build/man/ssm.8 b/doc/_build/man/ssm.8
++index 16ac1e8..ab777bf 100644
++--- a/doc/_build/man/ssm.8
+++++ b/doc/_build/man/ssm.8
++@@ -1,6 +1,6 @@
++ .\" Man page generated from reStructuredText.
++ .
++-.TH "SSM" "8" "August 07, 2013" "0.4" "System Storage Manager"
+++.TH "SSM" "8" "October 02, 2013" "0.4" "System Storage Manager"
++ .SH NAME
++ ssm \- System Storage Manager: a single tool to manage your storage
++ .
++@@ -59,9 +59,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
++ ..
++ .SH SYNOPSIS
++ .sp
++-\fBssm\fP [\fB\-h\fP] [\fB\-\-version\fP] [\fB\-v\fP] [\fB\-f\fP] [\fB\-b\fP BACKEND] {check,resize,create,list,add,remove,snapshot,mount} ...
+++\fBssm\fP [\fB\-h\fP] [\fB\-\-version\fP] [\fB\-v\fP] [\fB\-f\fP] [\fB\-b\fP BACKEND] [\fB\-n\fP] {check,resize,create,list,add,remove,snapshot,mount} ...
++ .sp
++-\fBssm\fP \fBcreate\fP [\fB\-h\fP] [\fB\-s\fP SIZE] [\fB\-n\fP NAME] [\fB\-\-fstype\fP FSTYPE] [\fB\-r\fP LEVEL] [\fB\-I\fP STRIPESIZE] [\fB\-i\fP STRIPES] [\fB\-p\fP POOL] [\fBdevice\fP [\fBdevice\fP ...]] [mount]
+++\fBssm\fP \fBcreate\fP [\fB\-h\fP] [\fB\-s\fP SIZE] [\fB\-n\fP NAME] [\fB\-\-fstype\fP FSTYPE] [\fB\-r\fP LEVEL] [\fB\-I\fP STRIPESIZE] [\fB\-i\fP STRIPES] [\fB\-p\fP POOL] [\fB\-e\fP [{luks,plain}]] [\fBdevice\fP [\fBdevice\fP ...]] [mount]
++ .sp
++ \fBssm\fP \fBlist\fP [\fB\-h\fP] [{volumes,vol,dev,devices,pool,pools,fs,filesystems,snap,snapshots}]
++ .sp
++@@ -110,7 +110,15 @@ or questions.
++ .TP
++ .BI \-b \ BACKEND\fP,\fB \ \-\-backend \ BACKEND
++ Choose backend to use. Currently you can choose from
++-(lvm,btrfs).
+++(lvm,btrfs,crypt).
+++.TP
+++.B \-n\fP,\fB \-\-dry\-run
+++Dry run. Do not do anything, just parse the command
+++line options and gather system information if
+++necessary. Note that with this option ssm will not
+++perform all the check as some of them are done by the
+++backends themselves. This option is mainly used for
+++debugging purposes.
++ .UNINDENT
++ .SH SYSTEM STORAGE MANAGER COMMANDS
++ .SS Introduction
++@@ -120,7 +128,7 @@ line as a first argument to the ssm. They all have specific use and its own
++ arguments, but global ssm arguments are propagated to all commands.
++ .SS Create command
++ .sp
++-\fBssm\fP \fBcreate\fP [\fB\-h\fP] [\fB\-s\fP SIZE] [\fB\-n\fP NAME] [\fB\-\-fstype\fP FSTYPE] [\fB\-r\fP LEVEL] [\fB\-I\fP STRIPESIZE] [\fB\-i\fP STRIPES] [\fB\-p\fP POOL] [\fBdevice\fP [\fBdevice\fP ...]] [mount]
+++\fBssm\fP \fBcreate\fP [\fB\-h\fP] [\fB\-s\fP SIZE] [\fB\-n\fP NAME] [\fB\-\-fstype\fP FSTYPE] [\fB\-r\fP LEVEL] [\fB\-I\fP STRIPESIZE] [\fB\-i\fP STRIPES] [\fB\-p\fP POOL] [\fB\-e\fP [{luks,plain}]] [\fBdevice\fP [\fBdevice\fP ...]] [mount]
++ .sp
++ This command creates a new volume with defined parameters. If \fBdevice\fP is
++ provided it will be used to create a volume, hence it will be added into the
++@@ -195,6 +203,12 @@ you have to specify RAID level as well.
++ .BI \-p \ POOL\fP,\fB \ \-\-pool \ POOL
++ Pool to use to create the new volume.
++ .UNINDENT
+++.INDENT 0.0
+++.TP
+++.B \-e [{luks,plain}], \-\-encrypt [{luks,plain}]
+++Create encrpted volume. Extension to use can be
+++specified.
+++.UNINDENT
++ .SS List command
++ .sp
++ \fBssm\fP \fBlist\fP [\fB\-h\fP] [{volumes,vol,dev,devices,pool,pools,fs,filesystems,snap,snapshots}]
++@@ -280,6 +294,9 @@ ask you whether you want to remove it from the original pool.
++ .sp
++ In some cases file system has to be mounted in order to resize. This will be
++ handled by \fBssm\fP automatically by mounting the \fBvolume\fP temporarily.
+++.sp
+++Note that resizing btrfs subvolume is not supported, only the whole file
+++system can be resized.
++ .INDENT 0.0
++ .TP
++ .B \-h\fP,\fB \-\-help
++@@ -435,8 +452,8 @@ devices specified and non existing pool name.
++ Volume in btrfs back\-end is actually just btrfs subvolume with the
++ exception of the first volume created on btrfs pool creation, which is
++ the file system itself. Subvolumes can only be created on btrfs file
++-system when the it is mounted, but user does not have to
++-worry about that, since \fBssm\fP will automatically mount the file
+++system when it is mounted, but user does not have to
+++worry about that since \fBssm\fP will automatically mount the file
++ system temporarily in order to create a new subvolume.
++ .sp
++ Volume name is used as subvolume path in the btrfs file system and every
++@@ -449,6 +466,10 @@ Btrfs volumes are only shown in the \fIlist\fP output, when the file system is
++ mounted, with the exception of the main btrfs volume \- the file system
++ itself.
++ .sp
+++Also note that btrfs volumes and subvolumes can not be resized. This is
+++mainly limitation of the btrfs tools which currently does not work
+++reliably.
+++.sp
++ New btrfs volume can be created with \fBcreate\fP command.
++ .TP
++ .B snapshot
++@@ -498,9 +519,41 @@ Lvm requires \fIphysical device\fP to be created on the device, but with
++ .UNINDENT
++ .SS Crypt backend
++ .sp
++-Crypt backend in \fBssm\fP is currently limited to only gather the information
++-about encrypted volumes in the system. You can not create or manage encrypted
++-volumes or pools, but it will be extended in the future.
+++Crypt backend in \fBssm\fP uses cryptsetup and dm\-crypt target to manage
+++encrypted volumes. Crypt backend can be used as a regular backend for
+++creating encrypted volumes on top of regular block devices, or even other
+++volumes (lvm or md volumes for example). Or it can be used to create
+++encrypted lvm volumes right away in a single step.
+++.sp
+++Only volumes can be created with crypt backend. This backend does not
+++support pooling and does not require special devices.
+++.INDENT 0.0
+++.TP
+++.B pool
+++Crypt backend does not support pooling it is not possible to create
+++crypt pool or add a device into a pool.
+++.TP
+++.B volume
+++Volume in crypt backend is the volume created by dm\-crypt which
+++represent the data on the original encrypted device in unencrypted form.
+++Crypt backend does not support pooling, so only one device can be used
+++to create crypt volume. It also does not support raid or any device
+++concatenation.
+++.sp
+++Currently two modes, or extensions are supported luks and plain. Luks
+++is used by default.For more information about the extensions please see
+++\fBcryptsetup\fP manual page.
+++.TP
+++.B snapshot
+++Crypt backend does not support snapshotting, however if the encrypted
+++volume is created on top of the lvm volume, the lvm volume itself can
+++be snapshotted. The snapshot can be then opened by using \fBcryptsetup\fP\&.
+++It is possible that this might change in the future so that \fBssm\fP will
+++be able to activate the volume directly without the extra step.
+++.TP
+++.B device
+++Crypt backend does not require any special device to be created on.
+++.UNINDENT
++ .SS MD backend
++ .sp
++ MD backend in \fBssm\fP is currently limited to only gather the information
++diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
++index 04e405c..a4aedda 100644
++--- a/ssmlib/backends/btrfs.py
+++++ b/ssmlib/backends/btrfs.py
++@@ -23,6 +23,7 @@ import sys
++ import datetime
++ from ssmlib import misc
++ from ssmlib import problem
+++from ssmlib.backends import template
++
++ __all__ = ["BtrfsVolume", "BtrfsPool", "BtrfsDev"]
++
++@@ -56,12 +57,11 @@ def get_btrfs_version():
++ BTRFS_VERSION = get_btrfs_version()
++
++
++-class Btrfs(object):
+++class Btrfs(template.Backend):
++
++- def __init__(self, options, data=None):
+++ def __init__(self, *args, **kwargs):
+++ super(Btrfs, self).__init__(*args, **kwargs)
++ self.type = 'btrfs'
++- self.data = data or {}
++- self.options = options
++ self.default_pool_name = SSM_BTRFS_DEFAULT_POOL
++ self._vol = {}
++ self._pool = {}
++@@ -69,13 +69,12 @@ class Btrfs(object):
++ self._snap = {}
++ self._subvolumes = {}
++ self._binary = misc.check_binary('btrfs')
++- self.problem = problem.ProblemSet(options)
++ self.modified_list_version = True
++
++ if not self._binary:
++ return
++
++- self.mounts = misc.get_mounts('/dev/')
+++ self.mounts = misc.get_mounts('btrfs')
++ command = ['btrfs', 'filesystem', 'show']
++ self.output = misc.run(command, stderr=False)[1]
++
++@@ -107,12 +106,12 @@ class Btrfs(object):
++ pool['uuid'] = vol['uuid'] = uuid
++
++ try:
++- fallback = False
++ vol['real_dev'] = misc.get_device_by_uuid(uuid)
++
++ if vol['real_dev'] in self.mounts:
++ pool['mount'] = self.mounts[vol['real_dev']]['mp']
++ vol['mount'] = self.mounts[vol['real_dev']]['mp']
+++
++ else:
++ for dev_i in self.mounts:
++ found = re.findall(r'{0}:/.*'.format(vol['real_dev']), dev_i)
++@@ -121,7 +120,6 @@ class Btrfs(object):
++ break
++ except OSError:
++ # udev is "hard-to-work-with" sometimes so this is fallback
++- fallback = True
++ vol['real_dev'] = ""
++
++ if label != 'none':
++@@ -140,7 +138,7 @@ class Btrfs(object):
++ dev['pool_name'] = pool_name
++
++ # Fallback in case we could not find real_dev by uuid
++- if fallback and 'mount' not in pool:
+++ if 'mount' not in pool:
++ if dev['dev_name'] in self.mounts:
++ pool['mount'] = self.mounts[dev['dev_name']]['mp']
++ vol['real_dev'] = dev['dev_name']
++@@ -153,6 +151,7 @@ class Btrfs(object):
++ found = re.findall(r'{0}:/.*'.format(dev['dev_name']), dev_i)
++ if found:
++ pool['mount'] = self.mounts[found[0]]['mp']
+++ vol['real_dev'] = found[0].split(':')[0]
++ break
++
++ dev_used = get_real_number(array[5])
++@@ -313,26 +312,18 @@ class Btrfs(object):
++ self._pool[pool['pool_name']] = pool
++ self._vol[vol['dev_name']] = vol
++
++- def __iter__(self):
++- for item in sorted(self.data.iterkeys()):
++- yield item
++-
++- def __getitem__(self, key):
++- if key in self.data.iterkeys():
++- return self.data[key]
++-
++ def _remove_filesystem(self, name):
++ if 'mount' in self._vol[name]:
++ if self.problem.check(self.problem.FS_MOUNTED,
++ [name, self._vol[name]['mount']]):
++- misc.do_umount(self._vol[name]['mount'])
+++ misc.do_umount(self._vol[name]['real_dev'], all_targets=True)
++ for dev in self._dev.itervalues():
++ if dev['pool_name'] != name:
++ continue
++ misc.wipefs(dev['dev_name'], 'btrfs')
++
++
++-class BtrfsVolume(Btrfs):
+++class BtrfsVolume(Btrfs, template.BackendVolume):
++
++ def __init__(self, *args, **kwargs):
++ super(BtrfsVolume, self).__init__(*args, **kwargs)
++@@ -374,11 +365,18 @@ class BtrfsVolume(Btrfs):
++ else:
++ self._remove_filesystem(vol)
++
+++ def check(self, vol):
+++ vol = self.data[vol]
+++ return self.run_btrfs(['check', vol['real_dev']])[0]
+++
++ def resize(self, vol, size, resize_fs=True):
++ vol = self.data[vol]
++ if 'mount' not in vol:
++ tmp = misc.temp_mount("UUID={0}".format(vol['uuid']))
++ vol['mount'] = tmp
+++ if 'subvolume' in vol and vol['subvolume'] is True:
+++ self.problem.check(self.problem.NOT_SUPPORTED,
+++ 'Resizing btrfs subvolume')
++ command = ['filesystem', 'resize', str(int(size)) + "K", vol['mount']]
++ self.run_btrfs(command)
++
++@@ -402,7 +400,7 @@ class BtrfsVolume(Btrfs):
++ self.run_btrfs(command)
++
++
++-class BtrfsDev(Btrfs):
+++class BtrfsDev(Btrfs, template.BackendDevice):
++
++ def __init__(self, *args, **kwargs):
++ super(BtrfsDev, self).__init__(*args, **kwargs)
++@@ -416,7 +414,7 @@ class BtrfsDev(Btrfs):
++ "achieve by removing {0}".format(devices))
++
++
++-class BtrfsPool(Btrfs):
+++class BtrfsPool(Btrfs, template.BackendPool):
++
++ def __init__(self, *args, **kwargs):
++ super(BtrfsPool, self).__init__(*args, **kwargs)
++@@ -425,7 +423,22 @@ class BtrfsPool(Btrfs):
++ else:
++ self.data = self._pool
++
++- def _create_filesystem(self, pool, name, devs, size=None, raid=None):
+++ def _can_btrfs_force(self):
+++ """
+++ This is just ridiculous. Unfortunately btrfs tools usually change
+++ behaviour and options without bumping version number. So we have
+++ to check whether btrfs allows to 'force' file system creation.
+++ """
+++ command=['mkfs.btrfs', '-f']
+++ output = misc.run(command, can_fail=True)[1]
+++ found = re.search('invalid option', output)
+++ if found:
+++ return False
+++ else:
+++ return True
+++
+++ def _create_filesystem(self, pool, name, devs, size=None, options=None):
+++ options = options or {}
++ if not devs:
++ raise Exception("To create btrfs volume, some devices must be " +
++ "provided")
++@@ -434,12 +447,12 @@ class BtrfsPool(Btrfs):
++ self.problem.check(self.problem.TOOL_MISSING, 'mkfs.btrfs')
++ command = ['mkfs.btrfs', '-L', name]
++
++- if raid:
++- if raid['level'] == '0':
+++ if 'raid' in options:
+++ if options['raid'] == '0':
++ command.extend(['-m', 'raid0', '-d', 'raid0'])
++- elif raid['level'] == '1':
+++ elif options['raid'] == '1':
++ command.extend(['-m', 'raid1', '-d', 'raid1'])
++- elif raid['level'] == '10':
+++ elif options['raid'] == '10':
++ command.extend(['-m', 'raid10', '-d', 'raid10'])
++ else:
++ raise Exception("Btrfs backed currently does not support " +
++@@ -455,7 +468,8 @@ class BtrfsPool(Btrfs):
++ # have tried to remove the device from the respective pool already.
++ # So at this point there should not be any useful signatures to
++ # speak of. However as I mentioned btrfs is broken, so force it.
++- command.extend(['-f'])
+++ if self._can_btrfs_force():
+++ command.extend(['-f'])
++ command.extend(devs)
++ misc.run(command, stdout=True)
++ misc.send_udev_event(devs[0], "change")
++@@ -507,10 +521,11 @@ class BtrfsPool(Btrfs):
++ self._remove_filesystem(pool)
++
++ def create(self, pool, size=None, name=None, devs=None,
++- raid=None):
+++ options=None):
+++ options = options or {}
++ if pool in self._pool:
++ vol = None
++- if size or raid:
+++ if size or 'raid' in options:
++ self.problem.warn("Only name, volume name and pool name " +
++ "can be specified when creating btrfs " +
++ "subvolume, the rest will be ignored")
++@@ -535,7 +550,7 @@ class BtrfsPool(Btrfs):
++ if name:
++ self.problem.warn("Creating new pool. Argument (--name " +
++ "{0}) will be ignored!".format(name))
++- vol = self._create_filesystem(pool, pool, devs, size, raid)
+++ vol = self._create_filesystem(pool, pool, devs, size, options)
++ return vol
++
++
++diff --git a/ssmlib/backends/crypt.py b/ssmlib/backends/crypt.py
++index 3c44f58..4aad266 100644
++--- a/ssmlib/backends/crypt.py
+++++ b/ssmlib/backends/crypt.py
++@@ -19,36 +19,147 @@
++
++ import re
++ import os
+++import stat
++ from ssmlib import misc
++ from ssmlib import problem
+++from ssmlib.backends import template
++
++ __all__ = ["DmCryptVolume"]
++
+++SUPPORTED_CRYPT = ['luks', 'plain']
+++CRYPT_SIGNATURES = ['crypto_LUKS']
+++CRYPT_DEFAULT_EXTENSION = "luks"
+++
++ try:
++ SSM_CRYPT_DEFAULT_POOL = os.environ['SSM_CRYPT_DEFAULT_POOL']
++ except KeyError:
++ SSM_CRYPT_DEFAULT_POOL = "crypt_pool"
++
++ try:
++- DM_DEV_DIR = os.environ['DM_DEV_DIR']
+++ SSM_CRYPT_DEFAULT_VOL_PREFIX = os.environ['SSM_CRYPT_DEFAULT_VOL_PREFIX']
++ except KeyError:
++- DM_DEV_DIR = "/dev"
++-
++-
++-class DmCryptVolume(object):
++-
++- def __init__(self, options, data=None):
+++ SSM_CRYPT_DEFAULT_VOL_PREFIX = "encrypted"
+++
+++# cryptsetup against my expectations does not take into account
+++# DM_DEV_DIR so set it to /dev pernamently for now.
+++#try:
+++# DM_DEV_DIR = os.environ['DM_DEV_DIR']
+++#except KeyError:
+++# DM_DEV_DIR = "/dev"
+++DM_DEV_DIR = "/dev"
+++MAX_DEVS = 999
+++
+++def get_cryptsetup_version():
+++ try:
+++ output = misc.run(['cryptsetup', '--version'], can_fail=True)[1]
+++ version = map(int, output.strip().split()[-1].split('.', 3))
+++ except (OSError, AttributeError):
+++ version = [0, 0, 0]
+++ return version
+++
+++CRYPTSETUP_VERSION = get_cryptsetup_version()
+++
+++class DmObject(template.Backend):
+++ def __init__(self, *args, **kwargs):
+++ super(DmObject, self).__init__(*args, **kwargs)
++ self.type = 'crypt'
++- self.data = data or {}
++- self.output = None
++- self.options = options
++ self.mounts = misc.get_mounts('{0}/mapper'.format(DM_DEV_DIR))
++ self.default_pool_name = SSM_CRYPT_DEFAULT_POOL
++- self.problem = problem.ProblemSet(options)
++
++ if not misc.check_binary('dmsetup') or \
++ not misc.check_binary('cryptsetup'):
++ return
+++
+++ def run_cryptsetup(self, command, stdout=True):
+++ if not misc.check_binary('cryptsetup'):
+++ self.problem.check(self.problem.TOOL_MISSING, 'cryptsetup')
+++ command.insert(0, "cryptsetup")
+++ return misc.run(command, stdout=stdout)
+++
+++
+++class DmCryptPool(DmObject, template.BackendPool):
+++ def __init__(self, *args, **kwargs):
+++ super(DmCryptPool, self).__init__(*args, **kwargs)
+++ '''
+++ pool = {'pool_name': self.default_pool_name,
+++ 'type': 'crypt',
+++ 'dev_count': '0',
+++ 'pool_free': '0',
+++ 'pool_used': '0',
+++ 'pool_size': '0',
+++ 'hide': True}
+++ self.data[self.default_pool_name] = pool
+++ '''
+++
+++
+++ def create(self, pool, size=None, name=None, devs=None,
+++ options=None):
+++
+++ if CRYPTSETUP_VERSION < [1, 6, 0]:
+++ msg = "You need at least cryptsetup version " + \
+++ "{0}. Creating encrypted volumes".format('1.6.0')
+++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
+++ options = options or {}
+++ if 'encrypt' in options:
+++ if options['encrypt'] is True:
+++ options['encrypt'] = CRYPT_DEFAULT_EXTENSION
+++ if options['encrypt'] not in SUPPORTED_CRYPT:
+++ self.problem.not_supported("Extension "
+++ "\'{0}\'".format(options['encrypt']))
+++ else:
+++ # So the options is not crypt specific. It's ok, just use defaults
+++ options['encrypt'] = CRYPT_DEFAULT_EXTENSION
+++
+++ if len(devs) > 1:
+++ self.problem.not_supported("Device concatenation" +
+++ " with \"crypt\" backend")
+++ if not name:
+++ name = self._generate_devname()
+++ device = devs[0]
+++ args = []
+++ command = []
+++ if self.options.verbose:
+++ args.append('-v')
+++ else:
+++ args.append('-q')
+++ if options['encrypt'] == "luks":
+++ command.extend(args)
+++ if self.options.force:
+++ command.append('--force-password')
+++ if self.options.interactive:
+++ command.append('-y')
+++ command.extend(['luksFormat', device])
+++ self.run_cryptsetup(command)
+++ command = []
+++ command.extend(args)
+++ command.append('open')
+++ if size:
+++ # Size is in KiB but cryptsetup accepts it in 512 byte blocks
+++ size = str(float(size) * 2).split('.')[0]
+++ command.extend(['--size', size])
+++ command.extend(['--type', options['encrypt'], device, name])
+++ self.run_cryptsetup(command)
+++ return "{0}/mapper/{1}".format(DM_DEV_DIR, name)
+++
+++ def _generate_devname(self):
+++ for i in range(1, MAX_DEVS):
+++ name = "{0}{1:0>{align}}".format(SSM_CRYPT_DEFAULT_VOL_PREFIX, i,
+++ align=len(str(MAX_DEVS)))
+++ path = "{0}/mapper/{1}".format(DM_DEV_DIR, name)
+++ try:
+++ if stat.S_ISBLK(os.stat(path).st_mode):
+++ continue
+++ except OSError:
+++ pass
+++ return name
+++ self.problem.error("Can not find proper device name. Specify one!")
+++
+++
+++class DmCryptVolume(DmObject, template.BackendVolume):
+++
+++ def __init__(self, *args, **kwargs):
+++ super(DmCryptVolume, self).__init__(*args, **kwargs)
+++
++ command = ['dmsetup', 'table']
++ self.output = misc.run(command, stderr=False)[1]
++ for line in self.output.split("\n"):
++@@ -63,9 +174,9 @@ class DmCryptVolume(object):
++ devname = re.sub(":$", "",
++ "{0}/mapper/{1}".format(DM_DEV_DIR, array[0]))
++ dm['dm_name'] = devname
++- dm['pool_name'] = 'dm-crypt'
++- dm['dev_name'] = misc.get_real_device(devname)
++- dm['real_dev'] = dm['dev_name']
+++ dm['pool_name'] = self.default_pool_name
+++ dm['dev_name'] = devname
+++ dm['real_dev'] = misc.get_real_device(devname)
++ if dm['real_dev'] in self.mounts:
++ dm['mount'] = self.mounts[dm['real_dev']]['mp']
++
++@@ -78,12 +189,6 @@ class DmCryptVolume(object):
++ self._parse_cryptsetup(command, dm)
++ self.data[dm['dev_name']] = dm
++
++- def run_cryptsetup(self, command, stdout=True):
++- if not self._binary:
++- self.problem.check(self.problem.TOOL_MISSING, 'cryptsetup')
++- command.insert(0, "cryptsetup")
++- return misc.run(command, stdout=stdout)
++-
++ def _parse_cryptsetup(self, cmd, dm):
++ self.output = misc.run(cmd, stderr=False)[1]
++ for line in self.output.split("\n"):
++@@ -97,19 +202,52 @@ class DmCryptVolume(object):
++ elif array[0].strip() == 'device:':
++ dm['crypt_device'] = array[1]
++
+++ def __getitem__(self, name):
+++ if name in self.data.iterkeys():
+++ return self.data[name]
+++ device = name
+++ if not os.path.exists(name):
+++ device = DM_DEV_DIR + "/" + name
+++ if not os.path.exists(device):
+++ return None
+++ device = misc.get_real_device(device)
+++ if device in self.data.iterkeys():
+++ return self.data[device]
+++ return None
+++
++ def remove(self, dm):
+++ vol = self[dm]
+++ if 'mount' in vol:
+++ if self.problem.check(self.problem.FS_MOUNTED,
+++ [vol['dev_name'], vol['mount']]):
+++ misc.do_umount(vol['mount'])
++ command = ['remove', dm]
++ self.run_cryptsetup(command)
+++ misc.wipefs(vol['crypt_device'], CRYPT_SIGNATURES)
++
++ def resize(self, dm, size, resize_fs=True):
++ size = str(int(size) * 2)
++ command = ['resize', '--size', size, dm]
++ self.run_cryptsetup(command)
++
++- def __iter__(self):
++- for item in sorted(self.data.iterkeys()):
++- yield item
++
++- def __getitem__(self, key):
++- if key in self.data.iterkeys():
++- return self.data[key]
+++class DmCryptDevice(DmObject, template.BackendDevice):
+++
+++ def __init__(self, *args, **kwargs):
+++ super(DmCryptDevice, self).__init__(*args, **kwargs)
+++
+++ for line in misc.get_partitions():
+++ device = {}
+++ devname = "/dev/" + line[3]
+++ signature = misc.get_signature(devname)
+++ if misc.get_signature(devname) in CRYPT_SIGNATURES:
+++ device['hide'] = False
+++ device['dev_name'] = devname
+++ device['pool_name'] = self.default_pool_name
+++ device['dev_free'] = '0'
+++ device['dev_used'] = str(misc.get_device_size(devname))
+++ self.data[devname] = device
+++
+++
+++ def remove(self, devices):
+++ misc.wipefs(device, CRYPT_SIGNATURES)
++diff --git a/ssmlib/backends/lvm.py b/ssmlib/backends/lvm.py
++index 9cc8079..955c705 100644
++--- a/ssmlib/backends/lvm.py
+++++ b/ssmlib/backends/lvm.py
++@@ -17,11 +17,13 @@
++
++ # lvm module for System Storage Manager
++
+++import re
++ import os
++ import stat
++ import datetime
++ from ssmlib import misc
++ from ssmlib import problem
+++from ssmlib.backends import template
++
++ __all__ = ["PvsInfo", "VgsInfo", "LvsInfo"]
++
++@@ -37,17 +39,32 @@ except KeyError:
++ MAX_LVS = 999
++
++
++-class LvmInfo(object):
+++def get_lvm_version():
+++ try:
+++ output = misc.run(['lvm', 'version'], can_fail=True)[1]
+++ output = output.strip().split("\n")
+++ pattern = re.compile("LVM version:")
+++ for line in output:
+++ if pattern.match(line.strip()):
+++ match = " ".join(line.split())
+++ tmp = re.search('(?<=LVM version: )\d+\.\d+\.\d+',
+++ match).group(0)
+++ version = map(int, tmp.split(".", 3))
+++ except (OSError, AttributeError):
+++ version = [0, 0, 0]
+++ return version
++
++- def __init__(self, options, data=None):
+++LVM_VERSION = get_lvm_version()
+++
+++
+++class LvmInfo(template.Backend):
+++
+++ def __init__(self, *args, **kwargs):
+++ super(LvmInfo, self).__init__(*args, **kwargs)
++ self.type = 'lvm'
++- self.data = data or {}
++ self.attrs = []
++- self.output = None
++- self.options = options
++ self.binary = misc.check_binary('lvm')
++ self.default_pool_name = SSM_LVM_DEFAULT_POOL
++- self.problem = problem.ProblemSet(options)
++
++ def run_lvm(self, command, noforce=False):
++ if not self.binary:
++@@ -59,9 +76,6 @@ class LvmInfo(object):
++ command.insert(0, "lvm")
++ misc.run(command, stdout=True)
++
++- def __str__(self):
++- return self.output
++-
++ def _data_index(self, row):
++ return row.values()[len(row.values()) - 1]
++
++@@ -86,16 +100,16 @@ class LvmInfo(object):
++ def _fill_aditional_info(self, row):
++ pass
++
++- def __iter__(self):
++- for item in sorted(self.data.iterkeys()):
++- yield item
+++ def supported_since(self, version, string):
+++ if version > LVM_VERSION:
+++ msg = "ERROR: You need at least lvm version " + \
+++ "{0}. Feature \"{1}\"".format(".".join(map(str, version)),
+++ string)
+++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
+++ return True
++
++- def __getitem__(self, key):
++- if key in self.data.iterkeys():
++- return self.data[key]
++
++-
++-class VgsInfo(LvmInfo):
+++class VgsInfo(LvmInfo, template.BackendPool):
++
++ def __init__(self, *args, **kwargs):
++ super(VgsInfo, self).__init__(*args, **kwargs)
++@@ -149,40 +163,85 @@ class VgsInfo(LvmInfo):
++ self.run_lvm(command)
++
++ def create(self, vg, size=None, name=None, devs=None,
++- raid=None):
+++ options=None):
+++ options = options or {}
++ devices = devs or []
++ command = ['lvcreate', vg]
++- if size:
++- command.extend(['-L', size + 'K'])
++- else:
++- if len(devices) > 0:
++- size = "100%PVS"
++- else:
++- size = "100%FREE"
++- command.extend(['-l', size])
++
++ if name:
++ lvname = name
++ else:
++ lvname = self._generate_lvname(vg)
++
+++ if size:
+++ command.extend(['-L', size + 'K'])
+++ else:
+++ if len(devices) > 0:
+++ tmp = "100%PVS"
+++ else:
+++ tmp = "100%FREE"
+++ command.extend(['-l', tmp])
+++
++ command.extend(['-n', lvname.rpartition("/")[-1]])
++
++- if raid:
++- if raid['level'] == '0':
++- if not raid['stripesize']:
++- raid['stripesize'] = "64"
++- if not raid['stripes'] and len(devices) > 0:
++- raid['stripes'] = str(len(devices))
++- if not raid['stripes']:
+++ if 'raid' in options:
+++ if options['raid'] == '0':
+++ if not options['stripesize']:
+++ options['stripesize'] = "64"
+++ if not options['stripes'] and len(devices) > 0:
+++ options['stripes'] = str(len(devices))
+++ if not options['stripes']:
++ self.problem.error("Devices or number of " +
++ "stripes should be defined!")
++- if raid['stripesize']:
++- command.extend(['-I', raid['stripesize']])
++- if raid['stripes']:
++- command.extend(['-i', raid['stripes']])
+++ if options['stripesize']:
+++ command.extend(['-I', options['stripesize']])
+++ if options['stripes']:
+++ command.extend(['-i', options['stripes']])
+++ elif options['raid'] == '1' and \
+++ self.supported_since([2,2,89],"raid1"):
+++ if options['stripesize'] or options['stripes']:
+++ msg = "ERROR: Specifying stripe size or number of " + \
+++ "stripes when creating raid1 volume with lvm backend"
+++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
+++ # Unfortunately 50%PVS does not work here because it does not
+++ # take in account metadata needed to create mirrored volume.
+++ # Using 49%PVS is not viable either because it will cut off
+++ # a lot of potential storage. So we'll require to specify
+++ # size in this case.
+++ if not size:
+++ msg = "ERROR: Creating raid1 with lvm backend without " + \
+++ "specifying size"
+++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
+++ command.extend(["--type", "raid1"])
+++ elif options['raid'] == '10' and \
+++ self.supported_since([2,2,98],"raid10"):
+++ if not options['stripesize']:
+++ options['stripesize'] = "64"
+++ if not options['stripes'] and len(devices) > 0:
+++ if len(devices) < 4:
+++ self.problem.error("Number of devices should be at " +
+++ "least 4 in raid 10 setup")
+++ if len(devices) % 2 != 0:
+++ self.problem.error("Number of devices should be " +
+++ "even in raid 10 setup")
+++ options['stripes'] = str(len(devices)/2)
+++ if not options['stripes']:
+++ self.problem.error("Devices or number of " +
+++ "stripes should be defined")
+++ if int(options['stripes']) < 2:
+++ self.problem.error("Number of stripes should be at " +
+++ "least 2 in raid 10 setup")
+++ if options['stripesize']:
+++ command.extend(['-I', options['stripesize']])
+++ if options['stripes']:
+++ command.extend(['-i', options['stripes']])
+++ if not size:
+++ msg = "ERROR: Creating raid10 with lvm backend without " + \
+++ "specifying size"
+++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
+++ command.extend(["--type", "raid10"])
++ else:
++- self.problem.not_supported("RAID level {0}".format(raid['level']) +
+++ self.problem.not_supported("RAID level {0}".format(options['raid']) +
++ " with \"lvm\" backend")
++
++ command.extend(devices)
++@@ -190,7 +249,7 @@ class VgsInfo(LvmInfo):
++ return "{0}/{1}/{2}".format(DM_DEV_DIR, vg, lvname)
++
++
++-class PvsInfo(LvmInfo):
+++class PvsInfo(LvmInfo, template.BackendDevice):
++
++ def __init__(self, *args, **kwargs):
++ super(PvsInfo, self).__init__(*args, **kwargs)
++@@ -221,7 +280,7 @@ class PvsInfo(LvmInfo):
++ self.run_lvm(command)
++
++
++-class LvsInfo(LvmInfo):
+++class LvsInfo(LvmInfo, template.BackendVolume):
++
++ def __init__(self, *args, **kwargs):
++ super(LvsInfo, self).__init__(*args, **kwargs)
++diff --git a/ssmlib/backends/md.py b/ssmlib/backends/md.py
++index 9e902d7..f33243c 100644
++--- a/ssmlib/backends/md.py
+++++ b/ssmlib/backends/md.py
++@@ -23,6 +23,7 @@ import socket
++ import datetime
++ from ssmlib import misc
++ from ssmlib import problem
+++from ssmlib.backends import template
++
++ try:
++ SSM_DM_DEFAULT_POOL = os.environ['SSM_DM_DEFAULT_POOL']
++@@ -32,15 +33,14 @@ except KeyError:
++ MDADM = "mdadm"
++
++
++-class MdRaid(object):
+++class MdRaid(template.Backend):
++
++- def __init__(self, options, data=None):
+++ def __init__(self, *args, **kwargs):
+++ super(MdRaid, self).__init__(*args, **kwargs)
++ self.type = 'dm'
++- self.data = data or {}
++ self._vol = {}
++ self._pool = {}
++ self._dev = {}
++- self.options = options
++ self.hostname = socket.gethostname()
++ self._binary = misc.check_binary(MDADM)
++ self.default_pool_name = SSM_DM_DEFAULT_POOL
++@@ -50,7 +50,6 @@ class MdRaid(object):
++ if not self._binary:
++ return
++
++- self.problem = problem.ProblemSet(options)
++ self.mounts = misc.get_mounts('/dev/md')
++
++ mdnumber = misc.get_dmnumber("md")
++@@ -109,16 +108,8 @@ class MdRaid(object):
++ command.insert(0, MDADM)
++ return misc.run(command, stdout=True)
++
++- def __iter__(self):
++- for item in sorted(self.data.iterkeys()):
++- yield item
++-
++- def __getitem__(self, key):
++- if key in self.data.iterkeys():
++- return self.data[key]
++-
++
++-class MdRaidVolume(MdRaid):
+++class MdRaidVolume(MdRaid, template.BackendVolume):
++
++ def __init__(self, *args, **kwargs):
++ super(MdRaidVolume, self).__init__(*args, **kwargs)
++@@ -135,7 +126,7 @@ class MdRaidVolume(MdRaid):
++ self.problem.not_supported("Resizing with \"md\" backend")
++
++
++-class MdRaidDevice(MdRaid):
+++class MdRaidDevice(MdRaid, template.BackendDevice):
++
++ def __init__(self, *args, **kwargs):
++ super(MdRaidDevice, self).__init__(*args, **kwargs)
++diff --git a/ssmlib/backends/template.py b/ssmlib/backends/template.py
++new file mode 100644
++index 0000000..94f4b40
++--- /dev/null
+++++ b/ssmlib/backends/template.py
++@@ -0,0 +1,95 @@
+++#!/usr/bin/env python
+++#
+++# (C)2013 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+++#
+++# This program is free software: you can redistribute it and/or modify
+++# it under the terms of the GNU General Public License as published by
+++# the Free Software Foundation, either version 2 of the License, or
+++# (at your option) any later version.
+++#
+++# This program is distributed in the hope that it will be useful,
+++# but WITHOUT ANY WARRANTY; without even the implied warranty of
+++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+++# GNU General Public License for more details.
+++#
+++# You should have received a copy of the GNU General Public License
+++# along with this program. If not, see <http://www.gnu.org/licenses/>.
+++
+++# template module for System Storage Manager contains template classes
+++# to use when creating new backend
+++
+++import os
+++from ssmlib import misc
+++from ssmlib import problem
+++
+++__all__ = ["Backend", "BackendPool", "BackendVolume", "BackendDevice"]
+++
+++try:
+++ SSM_TEMPLATE_DEFAULT_POOL = os.environ['SSM_TEMPLATE_DEFAULT_POOL']
+++except KeyError:
+++ SSM_TEMPLATE_DEFAULT_POOL = "template_pool"
+++
+++
+++class Backend(object):
+++ def __init__(self, options, data=None):
+++ self.type = 'template'
+++ self.data = data or {}
+++ self.options = options
+++ self.output = None
+++ self.default_pool_name = SSM_TEMPLATE_DEFAULT_POOL
+++ self.problem = problem.ProblemSet(options)
+++
+++ def __str__(self):
+++ return repr(self.data)
+++
+++ def __iter__(self):
+++ for item in sorted(self.data.iterkeys()):
+++ yield item
+++
+++ def __getitem__(self, key):
+++ if key in self.data.iterkeys():
+++ return self.data[key]
+++
+++
+++class BackendPool(Backend):
+++ def __init__(self, *args, **kwargs):
+++ super(BackendPool, self).__init__(*args, **kwargs)
+++
+++ def reduce(self, pool, device):
+++ self.problem.check(self.problem.NOT_SUPPORTED,
+++ "Reducing pool with {0} backend".format(self.type))
+++
+++ def new(self, pool, devices):
+++ self.problem.check(self.problem.NOT_SUPPORTED,
+++ "Creating new pool with {0} backend".format(self.type))
+++
+++ def extend(self, pool, devices):
+++ self.problem.check(self.problem.NOT_SUPPORTED,
+++ "Extending pool with {0} backend".format(self.type))
+++
+++ def remove(self, pool):
+++ self.problem.check(self.problem.NOT_SUPPORTED,
+++ "Removing pool with {0} backend".format(self.type))
+++
+++ def create(self, pool, size=None, name=None, devs=None,
+++ options=None):
+++ self.problem.check(self.problem.NOT_IMPLEMENTED,
+++ "Creating volume with {0} backend".format(self.type))
+++
+++
+++class BackendVolume(Backend):
+++ def __init__(self, *args, **kwargs):
+++ super(BackendVolume, self).__init__(*args, **kwargs)
+++
+++ def remove(self, volume):
+++ self.problem.check(self.problem.NOT_SUPPORTED,
+++ "Removing volume with {0} backend".format(self.type))
+++
+++
+++class BackendDevice(Backend):
+++ def __init__(self, *args, **kwargs):
+++ super(BackendDevice, self).__init__(*args, **kwargs)
+++
+++ def remove(self, devices):
+++ self.problem.check(self.problem.NOT_SUPPORTED,
+++ "Removing device with {0} backend".format(self.type))
++diff --git a/ssmlib/main.py b/ssmlib/main.py
++index ef9232d..7aab312 100644
++--- a/ssmlib/main.py
+++++ b/ssmlib/main.py
++@@ -30,7 +30,7 @@ from ssmlib.backends import lvm, crypt, btrfs, md
++
++ EXTN = ['ext2', 'ext3', 'ext4']
++ SUPPORTED_FS = ['xfs', 'btrfs'] + EXTN
++-SUPPORTED_BACKENDS = ['lvm', 'btrfs']
+++SUPPORTED_BACKENDS = ['lvm', 'btrfs', 'crypt']
++ SUPPORTED_RAID = ['0', '1', '10']
++ os.environ['LC_NUMERIC'] = "C"
++
++@@ -222,7 +222,7 @@ class FsInfo(object):
++ raise Exception("File system on {0} is not ".format(self.device) +
++ "clean, I will not attempt to resize it. Please," +
++ "fix the problem first.")
++- misc.run(command, stdout=True)
+++ return misc.run(command, stdout=True)[0]
++
++ def xfs_get_info(self, dev):
++ command = ["xfs_db", "-r", "-c", "sb", "-c", "print", dev]
++@@ -341,6 +341,11 @@ class DeviceInfo(object):
++ dev['mount'] = "PARTITIONED"
++ dev['type'] = 'disk'
++
+++ def remove(self, device):
+++ PR.error("It is not clear what do you want " +
+++ "to achieve by removing " +
+++ "{0}!".format(device))
+++
++ def set_globals(self, options):
++ self.options = options
++
++@@ -410,6 +415,9 @@ class Item(object):
++ else:
++ name = self.data['dev_name']
++ fs = FsInfo(name, self.obj.options)
+++ if 'fs_type' not in fs.data:
+++ # Not a file system
+++ return
++ try:
++ fs.mounted = self.data['mount']
++ except KeyError:
++@@ -639,6 +647,11 @@ class Pool(Storage):
++ except RuntimeError, err:
++ PR.warn(err)
++ PR.warn("Can not get information about btrfs pools")
+++ try:
+++ self._data['crypt'] = crypt.DmCryptPool(options=self.options)
+++ except RuntimeError, err:
+++ PR.warn(err)
+++ PR.warn("Can not get information about crypt pools")
++
++ backend = self.get_backend(SSM_DEFAULT_BACKEND)
++ self.default = Item(backend, backend.default_pool_name)
++@@ -682,10 +695,18 @@ class Devices(Storage):
++ PR.warn("Can not get information about MD devices")
++ my_md = Struct()
++ my_md.data = {}
+++ try:
+++ my_crypt = crypt.DmCryptDevice(options=self.options)
+++ except RuntimeError, err:
+++ PR.warn(err)
+++ PR.warn("Can not get information about crypt devices")
+++ my_crypt = Struct()
+++ my_crypt.data = {}
++
++ self._data['dev'] = DeviceInfo(data=dict(my_lvm.data.items() +
++ my_btrfs.data.items() +
++- my_md.data.items()),
+++ my_md.data.items() +
+++ my_crypt.data.items()),
++ options=self.options)
++ self.header = ['Device', 'Free', 'Used',
++ 'Total', 'Pool', 'Mount point']
++@@ -862,30 +883,41 @@ class StorageHandle(object):
++ def check(self, args):
++ """
++ Check the file system on the volume. FsInfo is used for that purpose,
++- except for btrfs.
+++ except for btrfs. Or check the volume itself if backend supports it.
++ """
++ err = 0
++ checked = 0
++- for fs in args.device:
++- print "Checking {0} file system on \'{1}\':".format(fs.fstype,
++- fs.device),
++- if fs.mounted:
++- print
+++ for dev in args.device:
+++ if 'mount' in dev:
++ try:
++- if PR.check(PR.FS_MOUNTED, [fs.device, fs.mounted]):
++- misc.do_umount(fs.device)
+++ if PR.check(PR.FS_MOUNTED, [dev['real_dev'], dev['mount']]):
+++ misc.do_umount(dev['real_dev'])
++ except problem.FsMounted:
++ PR.warn("Unable to check file system " +
++- "\'{0}\' on volume \'{1}\'".format(fs.fstype,
++- fs.device))
+++ "\'{0}\' on volume \'{1}\'".format(dev['fs_type'],
+++ dev['real_dev']))
++ continue
++- ret = fs.fsck()
++- checked += 1
++- err += ret
++- if ret:
++- print "FAIL"
++- else:
++- print "OK"
+++
+++ # Does backend support check ?
+++ try:
+++ if getattr(dev, "check"):
+++ print "Checking volume \'{0}\'.".format(dev['real_dev'])
+++ ret = dev.check()
+++ checked += 1
+++ err += ret
+++ if ret:
+++ continue
+++ except AttributeError:
+++ pass
+++
+++ # Do we have a file system to check ?
+++ if 'fs_info' in dev:
+++ fs = dev['fs_info']
+++ print "Checking {0} file system on \'{1}\'.".format(fs.fstype,
+++ fs.device)
+++ ret = fs.fsck()
+++ checked += 1
+++ err += ret
++ if checked == 0:
++ PR.error("Nothing was checked")
++ if err > 0:
++@@ -944,6 +976,14 @@ class StorageHandle(object):
++ if not self.dev[dev] or 'pool_name' not in self.dev[dev]:
++ # Check signature of existing file system on the device
++ # and ask user whether to use it or not.
+++ if self.dev[dev] and 'mount' in self.dev[dev]:
+++ try:
+++ if PR.check(PR.FS_MOUNTED,
+++ [dev, self.dev[dev]['mount']]):
+++ misc.do_umount(dev)
+++ except problem.FsMounted:
+++ args.device.remove(dev)
+++ continue
++ signature = misc.get_fs_type(dev)
++ if signature and \
++ not PR.check(PR.EXISTING_FILESYSTEM, [signature, dev]):
++@@ -977,6 +1017,10 @@ class StorageHandle(object):
++ args.pool = self.pool[args.volume['pool_name']]
++ vol_size = float(args.volume['vol_size'])
++
+++ if args.pool.type == 'btrfs':
+++ msg = "Resizing btrfs volume is not supported"
+++ raise problem.NotSupported(msg)
+++
++ if not args.size:
++ new_size = vol_size
++ elif args.size[0] == '+':
++@@ -987,7 +1031,7 @@ class StorageHandle(object):
++ new_size = float(args.size)
++ size_change = new_size - vol_size
++
++- fs = True if 'fs_type' in args.volume else False
+++ fs = True if 'fs_info' in args.volume else False
++
++ if new_size <= 0:
++ PR.error("New volume size \'{0} KB\' is too small".format(new_size))
++@@ -996,28 +1040,39 @@ class StorageHandle(object):
++ # Try to grow the file system, since there is nothing to
++ # do with the volume itself.
++ if fs:
++- args.volume['fs_info'].resize()
+++ ret = args.volume['fs_info'].resize()
+++ if ret:
+++ PR.error("File system on {0} can not be resized".format(args.volume.name))
++ else:
++ PR.check(PR.RESIZE_ALREADY_MATCH, [args.volume.name, new_size])
++ return
++
++ # Backend might not support pooling
++ if args.pool is None:
++- pool_free = None
+++ pool_free = 0.0
++ pool_name = "none"
++ else:
++ pool_free = float(args.pool['pool_free'])
++ pool_name = args.pool.name
++
++- have_size, devices = self._filter_device_list(args,
++- pool_free,
++- new_size)
+++ # No need to do anything with provided devices since
+++ # we do have enough space to cover the resize
+++ if (pool_free < size_change):
+++ have_size, devices = self._filter_device_list(args,
+++ pool_free,
+++ new_size)
+++ else:
+++ have_size = pool_free
++
++ if have_size < size_change:
++ PR.check(PR.RESIZE_NOT_ENOUGH_SPACE,
++ [pool_name, args.volume.name, new_size])
++- elif len(args.device) > 0 and new_size > vol_size:
++- self.add(args, True)
+++ elif len(args.device) > 0 and size_change > pool_free:
+++ try:
+++ self.add(args, True)
+++ except problem.NotSupported:
+++ # Some backends might not support pooling at all.
+++ pass
++
++ if new_size != vol_size:
++ args.volume.resize(new_size, fs)
++@@ -1028,9 +1083,30 @@ class StorageHandle(object):
++ provided as arguments. If the device is not in the selected pool, then
++ add() is called on the pool prior to create().
++ """
+++
+++ lvname = self.create_volume(args)
+++
+++ if args.encrypt and misc.is_bdevice(lvname) and \
+++ SSM_DEFAULT_BACKEND != 'crypt':
+++ crypt = self.pool.get_backend("crypt")
+++ args.pool = Item(crypt, crypt.default_pool_name)
+++ options = {'encrypt': args.encrypt}
+++ lvname = args.pool.create(devs=[lvname],
+++ size=None,
+++ options=options,
+++ name=args.name)
+++
+++ if args.fstype and args.pool.type != 'btrfs':
+++ if self._create_fs(args.fstype, lvname) != 0:
+++ self._mpoint = None
+++ if self._mpoint:
+++ self.reinit_vol()
+++ self._do_mount(self.vol[lvname])
+++
+++ def create_volume(self, args):
++ # Get the size in kilobytes
++- if args.size:
++- args.size = misc.get_real_size(args.size)
+++# if args.size:
+++# args.size = misc.get_real_size(args.size)
++
++ if self._mpoint and not (args.fstype or args.pool.type == 'btrfs'):
++ if PR.check(PR.CREATE_MOUNT_NOFS, self._mpoint):
++@@ -1049,6 +1125,12 @@ class StorageHandle(object):
++ have_size, devices = self._filter_device_list(args, pool_free,
++ args.size)
++
+++ # When the pool does not exist and there is no device usable
+++ # for creating the new pool, then there is no point of trying to
+++ # create a volume, since it would fail in the backend anyway.
+++ if not args.pool.exists() and len(devices) == 0:
+++ PR.check(PR.NO_DEVICES, args.pool.name)
+++
++ # Currently we do not allow setting subvolume size with btrfs. This
++ # should change in the future (quotas maybe) so the check should
++ # be removed or pushed to the backend itself.
++@@ -1058,12 +1140,6 @@ class StorageHandle(object):
++ [have_size, args.pool.name]):
++ args.size = None
++
++- # When the pool does not exist and there is no device usable
++- # for creating the new pool, then there is no point of trying to
++- # create a volume, since it would fail in the backend anyway.
++- if not args.pool.exists() and len(devices) == 0:
++- PR.check(PR.NO_DEVICES, args.pool.name)
++-
++ if have_size == 0:
++ PR.error("Not enough space ({0} KB) to".format(have_size) +
++ "to create volume")
++@@ -1080,32 +1156,52 @@ class StorageHandle(object):
++ "must not exceed number of devices " +
++ "({0})".format(tmp))
++
+++ if args.raid:
+++ # In raid we might has a requirement on a number of devices
+++ # available as well as different requirements on the size
+++ # available. We do not do any complicated math to figure out
+++ # whether we really do have enough space on the devices which
+++ # might differ in size. Let the respective backend tool deal
+++ # with that, it's always ok to fail, but we might cover the
+++ # most obvious cases here.
+++ dev_count = len(args.device)
+++ if args.pool.exists():
+++ dev_count += int(args.pool['dev_count'])
+++ if args.raid == '10' and args.stripes:
+++ if args.stripes * 2 > dev_count:
+++ PR.error("Not enough devices ({0}) ".format(dev_count) +
+++ "for specified number of ".format(args.stripes) +
+++ "stripes ({0}). You need ".format(args.stripes) +
+++ "at least {0} devices".format(args.stripes * 2))
+++ if args.raid == '1' and dev_count < 2:
+++ PR.error("You need at least 2 devices to create " +
+++ "raid1 volume")
+++
++ # If we want btrfs pool and it does not exist yet, we do not
++ # want to call add since it would create it. Note that when
++ # btrfs pool is created the new btrfs volume is created as well
++ # because it is actually the same thing
++ if len(args.device) > 0 and \
++ not (not args.pool.exists() and args.pool.type == 'btrfs'):
++- self.add(args, True)
+++ try:
+++ self.add(args, True)
+++ except problem.NotSupported:
+++ # Some backends might not support pooling at all.
+++ pass
++
+++ options = {}
+++ if args.encrypt:
+++ options['encrypt'] = args.encrypt
++ if args.raid:
++- raid = {'level': args.raid,
++- 'stripesize': args.stripesize,
++- 'stripes': args.stripes}
++- else:
++- raid = None
+++ options['raid'] = args.raid
+++ options['stripesize'] = args.stripesize
+++ options['stripes'] = args.stripes
++
++ lvname = args.pool.create(devs=devices,
++ size=args.size,
++- raid=raid,
+++ options=options,
++ name=args.name)
++-
++- if args.fstype and args.pool.type != 'btrfs':
++- if self._create_fs(args.fstype, lvname) != 0:
++- self._mpoint = None
++- if self._mpoint:
++- self.reinit_vol()
++- self._do_mount(self.vol[lvname])
+++ return lvname
++
++ def list(self, args):
++ """
++@@ -1150,11 +1246,20 @@ class StorageHandle(object):
++ else:
++ # Check signature of existing file system on the device
++ # and as user whether to use it or not.
++- signature = misc.get_fs_type(dev)
++- if signature and \
++- not PR.check(PR.EXISTING_FILESYSTEM, [signature, dev]):
++- args.device.remove(dev)
++- continue
+++ if item and 'mount' in item:
+++ try:
+++ if PR.check(PR.FS_MOUNTED, [dev, item['mount']]):
+++ misc.do_umount(dev)
+++ except problem.FsMounted:
+++ args.device.remove(dev)
+++ continue
+++ else:
+++ signature = misc.get_fs_type(dev)
+++ if signature and \
+++ not PR.check(PR.EXISTING_FILESYSTEM,
+++ [signature, dev]):
+++ args.device.remove(dev)
+++ continue
++
++ if args.pool.exists():
++ if len(args.device) > 0:
++@@ -1196,10 +1301,7 @@ class StorageHandle(object):
++ pool.reduce(item.name)
++ removed += 1
++ continue
++- else:
++- PR.error("It is not clear what do you want " +
++- "to achieve by removing " +
++- "{0}!".format(item.name))
+++
++ item.remove()
++ removed += 1
++ except (RuntimeError, problem.SsmError), ex:
++@@ -1222,7 +1324,8 @@ class StorageHandle(object):
++ snap_size = vol_size * 0.20
++ user_set_size = False
++ else:
++- snap_size = float(misc.get_real_size(args.size))
+++ snap_size = float(args.size)
+++ #snap_size = float(misc.get_real_size(args.size))
++ user_set_size = True
++
++ if pool_free < snap_size:
++@@ -1249,17 +1352,34 @@ class StorageHandle(object):
++ "{0} with options \'{1}\'".format(args.directory,
++ args.options))
++
+++ def can_check(self, device):
+++ fs = self.is_fs(device)
+++ if fs is False:
+++ real = misc.get_real_device(device)
+++ vol = self.vol[real]
+++ err = "'{0}' is not valid volume to check.".format(device)
+++ try:
+++ if not getattr(vol, "check"):
+++ raise argparse.ArgumentTypeError(err)
+++ else:
+++ return vol
+++ except AttributeError:
+++ raise argparse.ArgumentTypeError(err)
+++ else:
+++ return fs
+++
+++
++ def is_fs(self, device):
++ real = misc.get_real_device(device)
++
++ vol = self.vol[real]
++- if vol and 'fs_type' in vol:
++- return vol['fs_info']
+++ if vol and 'fs_info' in vol:
+++ return vol
++ dev = self.dev[real]
++- if dev and 'fs_type' in dev:
++- return dev['fs_info']
+++ if dev and 'fs_info' in dev:
+++ return dev
++ err = "'{0}' does not contain valid file system".format(real)
++- raise argparse.ArgumentTypeError(err)
+++ return False
++
++ def _find_device_record(self, path):
++ """
++@@ -1294,7 +1414,10 @@ class StorageHandle(object):
++ return self.get_bdevice(path)
++
++ def get_bdevice(self, path):
++- path = is_bdevice(path)
+++ path = misc.is_bdevice(path)
+++ if path == False:
+++ err = "'{0}' is not valid block device".format(path)
+++ raise argparse.ArgumentTypeError(err)
++ return self._find_device_record(path)
++
++ def is_pool(self, string):
++@@ -1310,7 +1433,7 @@ class StorageHandle(object):
++ if vol:
++ return vol
++ dev = self.dev[string]
++- if dev and 'fs_type' in dev:
+++ if dev and 'fs_info' in dev:
++ return dev
++ err = "'{0}' is not a valid volume to resize".format(string)
++ raise argparse.ArgumentTypeError(err)
++@@ -1366,6 +1489,21 @@ class StorageHandle(object):
++ err = "'{0}' is not valid pool nor volume".format(string)
++ raise argparse.ArgumentTypeError(err)
++
+++def valid_size(size):
+++ """ Validate that the 'size' is usable size argument. This is almost the
+++ same as valid_resize_size() except we do not allow '+' and '-' signs
+++ """
+++
+++ err = "'{0}' is not valid size.".format(size)
+++ if len(size) and size[0] in ['+', '-']:
+++ raise argparse.ArgumentTypeError(err)
+++ try:
+++ ret = misc.get_real_size(size)
+++ if float(ret) < 0:
+++ raise argparse.ArgumentTypeError(err)
+++ except:
+++ raise argparse.ArgumentTypeError(err)
+++ return ret
++
++ def valid_resize_size(size):
++ """
++@@ -1404,7 +1542,9 @@ def valid_resize_size(size):
++ raise argparse.ArgumentTypeError(err)
++
++
++-def is_directory(self, string):
+++def is_directory(string):
+++ if string is None:
+++ raise argparse.ArgumentTypeError(err)
++ try:
++ mode = os.stat(string).st_mode
++ except OSError:
++@@ -1417,19 +1557,6 @@ def is_directory(self, string):
++ raise argparse.ArgumentTypeError(err)
++
++
++-def is_bdevice(path):
++- path = misc.get_real_device(path)
++- try:
++- mode = os.lstat(path).st_mode
++- except OSError:
++- err = "'{0}' is not valid block device".format(path)
++- raise argparse.ArgumentTypeError(err)
++- if not stat.S_ISBLK(mode):
++- err = "'{0}' is not valid block device".format(path)
++- raise argparse.ArgumentTypeError(err)
++- return path
++-
++-
++ def is_supported_fs(fs):
++ if fs in SUPPORTED_FS:
++ return fs
++@@ -1484,6 +1611,14 @@ class SsmParser(object):
++ "({0}).".format(",".join(SUPPORTED_BACKENDS)),
++ choices=SUPPORTED_BACKENDS,
++ action=SetBackend)
+++ parser.add_argument('-n', '--dry-run',
+++ help='''Dry run. Do not do anything, just parse the command
+++ line options and gather system information if necessary.
+++ Note that with this option ssm will not perform all the
+++ check as some of them are done by the backends
+++ themselves. This option is mainly used for debugging
+++ purposes.''',
+++ action="store_true")
++ return parser
++
++ def _get_parser_check(self):
++@@ -1494,7 +1629,7 @@ class SsmParser(object):
++ help="Check consistency of the file system on the device.")
++ parser_check.add_argument('device', nargs='+',
++ help="Device with file system to check.",
++- type=self.storage.is_fs)
+++ type=self.storage.can_check)
++ parser_check.set_defaults(func=self.storage.check)
++ return parser_check
++
++@@ -1535,7 +1670,8 @@ class SsmParser(object):
++ A size suffix K|k, M|m, G|g, T|t, P|p, E|e can be used
++ to define 'power of two' units. If no unit is provided, it
++ defaults to kilobytes. This is optional if if
++- not given maximum possible size will be used.''')
+++ not given maximum possible size will be used.''',
+++ type=valid_size)
++ parser_create.add_argument('-n', '--name',
++ help='''The name for the new logical volume. This is optional
++ and if omitted, name will be generated by the
++@@ -1571,6 +1707,10 @@ class SsmParser(object):
++ parser_create.add_argument('-p', '--pool', default="",
++ help="Pool to use to create the new volume.",
++ type=self.storage.is_pool)
+++ parser_create.add_argument('-e', '--encrypt', nargs='?',
+++ choices=crypt.SUPPORTED_CRYPT, const=True,
+++ help='''Create encrpted volume. Extension to use can be
+++ specified.''')
++ parser_create.add_argument('device', nargs='*',
++ help='''Devices to use for creating the volume. If the device
++ is not in any pool, it is added into the pool prior the
++@@ -1637,7 +1777,8 @@ class SsmParser(object):
++ A size suffix K|k, M|m, G|g, T|t, P|p, E|e can be used
++ to define 'power of two' units. If no unit is provided, it
++ defaults to kilobytes. This is option and if not give,
++- the size will be determined automatically.''')
+++ the size will be determined automatically.''',
+++ type=valid_size)
++ group = parser_snapshot.add_mutually_exclusive_group()
++ group.add_argument('-d', '--dest',
++ help='''Destination of the snapshot specified with absolute
++@@ -1705,6 +1846,9 @@ def main(args=None):
++ # Register clean-up function on exit
++ sys.exitfunc = misc.do_cleanup
++
+++ if args.dry_run:
+++ return 0;
+++
++ try:
++ args.func(args)
++ except argparse.ArgumentTypeError, ex:
++diff --git a/ssmlib/misc.py b/ssmlib/misc.py
++index 8e731ff..3ff1577 100644
++--- a/ssmlib/misc.py
+++++ b/ssmlib/misc.py
++@@ -20,6 +20,7 @@
++ import os
++ import re
++ import sys
+++import stat
++ import tempfile
++ import threading
++ import subprocess
++@@ -191,13 +192,15 @@ def do_mount(device, directory, options=None):
++ run(command)
++
++
++-def do_umount(mpoint):
++- command = ['umount', mpoint]
+++def do_umount(mpoint, all_targets=False):
+++ command = ['umount']
+++ if all_targets:
+++ command.append('--all-targets')
++ try:
++- run(command)
+++ run(command + [mpoint])
++ except RuntimeError:
++- command = ['umount', '-l', mpoint]
++- run(command)
+++ command.append('-l')
+++ run(command + mpoint)
++
++
++ def temp_mount(device, options=None):
++@@ -228,12 +231,13 @@ def get_signature(device, types=None):
++ command.extend(['-u', types])
++ command.append(device)
++
++- output = run(command, can_fail=True)[1].strip()
+++ ret, output = run(command, can_fail=True, stderr=False)
+++ output = output.strip()
++
++- if len(output) > 0:
++- return output
++- else:
+++ if ret:
++ return None
+++ else:
+++ return output
++
++
++ def get_fs_type(device):
++@@ -319,16 +323,21 @@ def get_dmnumber(name):
++ return dmnumber
++
++
++-def wipefs(device, typ):
+++def wipefs(device, signatures):
+++ if type(signatures) is not list:
+++ signatures = [signatures]
++ command = ['wipefs', '-p', device]
++ output = run(command)[1]
+++ offset = []
++ for line in output[1:].split('\n'):
++ if not line:
++ continue
++ array = line.split(",")
++- if array[-1] == typ:
++- command = ['wipefs', '--offset', array[0], device]
++- run(command)
+++ if array[-1] in signatures:
+++ offset.extend(['--offset', array[0]])
+++ if len(offset) > 1:
+++ command = ['wipefs'] + offset + [device]
+++ run(command)
++
++
++ def humanize_size(arg):
++@@ -545,3 +554,26 @@ def terminal_size(default=(25, 80)):
++ if not cr:
++ cr = (25, 80)
++ return int(cr[1]), int(cr[0])
+++
+++
+++def is_bdevice(path):
+++ """
+++ Check whether the path is block device. If it is return
+++ the path to the real block device, otherwise return False
+++ """
+++ path = get_real_device(path)
+++ try:
+++ mode = os.lstat(path).st_mode
+++ except OSError:
+++ return False
+++ if not stat.S_ISBLK(mode):
+++ return False
+++ return path
+++
+++
+++def get_device_size(device):
+++ devname = device.split('/')[-1]
+++ with open("/sys/block/{0}/size".format(devname), 'r') as f:
+++ for line in f:
+++ size = int(line)/2
+++ return size
++diff --git a/ssmlib/problem.py b/ssmlib/problem.py
++index 56846df..dfa6ec6 100644
++--- a/ssmlib/problem.py
+++++ b/ssmlib/problem.py
++@@ -22,7 +22,8 @@ import sys
++ __all__ = ["ProblemSet", "SsmError", "GeneralError", "ProgrammingError",
++ "BadEnvVariable", "NotEnoughSpace", "ResizeMatch", "FsNotSpecified",
++ "DeviceUsed", "ExistingFilesystem", "NoDevices", "ToolMissing",
++- "CanNotRun", "CommandFailed", "UserInterrupted", "NotSupported"]
+++ "CanNotRun", "CommandFailed", "UserInterrupted", "NotSupported",
+++ "NotImplemented"]
++
++ # Define prompt codes
++ PROMPT_NONE = 0
++@@ -52,9 +53,10 @@ FL_DEFAULT_NO = 16
++ FL_SILENT = 32
++ FL_EXIT_ON_NO = 64
++ FL_EXIT_ON_YES = 128
++-FL_FATAL = 256
+++FL_NO_MESSAGE = 256
++ FL_FORCE_YES = 512
++ FL_FORCE_NO = 1024
+++FL_FATAL = (2048 | FL_NO_MESSAGE)
++
++
++ class SsmError(Exception):
++@@ -65,7 +67,7 @@ class SsmError(Exception):
++ self.errcode = errcode
++
++ def __str__(self):
++- return repr("Error ({0}): {1}".format(self.errcode, self.msg))
+++ return "SSM Error ({0}): {1}".format(self.errcode, self.msg)
++
++
++ class GeneralError(SsmError):
++@@ -142,6 +144,10 @@ class ExistingFilesystem(SsmError):
++ def __init__(self, msg, errcode=2015):
++ super(ExistingFilesystem, self).__init__(msg, errcode)
++
+++class NotImplemented(SsmError):
+++ def __init__(self, msg, errcode=2016):
+++ super(NotImplemented, self).__init__(msg, errcode)
+++
++
++ class ProblemSet(object):
++
++@@ -158,7 +164,7 @@ class ProblemSet(object):
++ PROMPT_NONE, FL_FATAL, ProgrammingError]
++
++ self.GENERAL_ERROR = \
++- ['SSM Error: {0}!', PROMPT_NONE, FL_FATAL, GeneralError]
+++ ['{0}!', PROMPT_NONE, FL_FATAL, GeneralError]
++
++ self.GENERAL_INFO = \
++ ['SSM Info: {0}', PROMPT_NONE, FL_NONE, None]
++@@ -222,11 +228,17 @@ class ProblemSet(object):
++ ['{0} is not supported!',
++ PROMPT_NONE, FL_FATAL, NotSupported]
++
+++ self.NOT_IMPLEMENTED = \
+++ ['\'{0}\' function is not implemented by {1}!',
+++ PROMPT_NONE, FL_FATAL, NotImplemented]
+++
++ def _can_print_message(self, flags):
++ if (flags & FL_DEBUG_ONLY):
++ return self.options.debug
++ elif (flags & FL_VERBOSE_ONLY):
++ return self.options.verbose
+++ elif (flags & FL_NO_MESSAGE):
+++ return False
++ else:
++ return True
++
++@@ -289,14 +301,10 @@ class ProblemSet(object):
++
++ if self._can_print_message(flags) and \
++ (flags & FL_MSG_ONLY or prompt_msg is None):
++- print >> sys.stderr, message,
++- else:
++- print message,
+++ print >> sys.stderr, message
++ if not flags & FL_MSG_ONLY and prompt_msg is not None:
++- print '{0}'.format(prompt_msg),
+++ print message, '{0}'.format(prompt_msg),
++ res = self._ask_question(flags)
++- else:
++- print >> sys.stderr
++
++ if (flags & FL_FATAL):
++ if exc:
++diff --git a/tests/bashtests/001-lvm-add.sh b/tests/bashtests/001-lvm-add.sh
++index fee51a8..cfe95da 100755
++--- a/tests/bashtests/001-lvm-add.sh
+++++ b/tests/bashtests/001-lvm-add.sh
++@@ -131,6 +131,16 @@ mkfs.ext3 $dev1
++ # Default answer is No
++ not ssm add $dev1
++ not check vg_field $SSM_LVM_DEFAULT_POOL pv_count 1
+++# It can be forced though
+++ssm -f add $dev1
+++check vg_field $SSM_LVM_DEFAULT_POOL pv_count 1
+++ssm -f remove $SSM_LVM_DEFAULT_POOL
+++
+++# Try to use device with existing file system
+++mkfs.ext3 $dev1
+++# Default answer is No
+++not ssm add $dev1
+++not check vg_field $SSM_LVM_DEFAULT_POOL pv_count 1
++ ssm add $dev1 $dev2
++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 1
++ ssm -f remove --all
++diff --git a/tests/bashtests/002-lvm-create.sh b/tests/bashtests/002-lvm-create.sh
++index aceea6d..d316e5d 100755
++--- a/tests/bashtests/002-lvm-create.sh
+++++ b/tests/bashtests/002-lvm-create.sh
++@@ -115,6 +115,64 @@ check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 10 none none 960.0
++ check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL 960.00MB striped
++ ssm -f remove $SSM_LVM_DEFAULT_POOL
++
+++# Create a raid 1 logical volume
+++# Size of the volume must be specified for lvm raid1
+++not ssm create -r 1 $dev1 $dev2
+++size=$((DEV_SIZE/2))
+++size=$(align_size_up $size)
+++ssm create -s ${size}M -r 1 $dev1 $dev2
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 segtype raid1
+++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 2 none none 192.00MB
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid1
+++
+++ssm add $TEST_DEVS
+++size=$((DEV_SIZE*2))
+++size=$(align_size_up $size)
+++ssm create -s ${size}M -r 1
+++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 10 none none 960.00MB
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol2 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid1
+++ssm -f remove $SSM_LVM_DEFAULT_POOL
+++
+++# Create a raid 10 logical volume
+++# Size of the volume must be specified for lvm raid10
+++not ssm create -r 10 $dev1 $dev2 $dev3 $dev4
+++# Minimum is 4 devices
+++not ssm create -s ${DEV_SIZE}M -r 10 $dev1 $dev2
+++# Number of device has to be even
+++not ssm create -s ${DEV_SIZE}M -r 10 $dev1 $dev2 $dev3 $dev4 $dev5
+++ssm remove $dev5
+++ssm create -s ${DEV_SIZE}M -r 10 $dev1 $dev2 $dev3 $dev4
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 stripes 4
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 segtype raid10
+++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 4 none none 384.00MB
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL 104.00MB raid1
+++size=56
+++# Stripes or device has to be specified
+++not ssm create -s ${size}M -r 10
+++ssm create -s ${size}M -r 10 --stripes 2
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol2 stripes 4
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol2 segtype raid10
+++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 4 none none 384.00MB
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol2 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid10
+++ssm add $TEST_DEVS
+++size=60
+++ssm create -s ${size}M -r 10 --stripes 3
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol3 stripes 6
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol3 segtype raid10
+++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 10 none none 960.00MB
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol3 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid10
+++ssm -f remove $SSM_LVM_DEFAULT_POOL
+++ssm add $TEST_DEVS
+++size=$((DEV_SIZE*2))
+++size=$(align_size_up $size)
+++ssm create -s ${size}M -r 10 $TEST_DEVS
+++ssm list
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 stripes 10
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 segtype raid10
+++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 10 none none 960.00MB
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid10
+++ssm -f remove $SSM_LVM_DEFAULT_POOL
+++
++ # Create several volumes with different parameters
++ ssm add $TEST_DEVS
++ not ssm create -I 8 -i $(($DEV_COUNT/2)) -s $(($DEV_SIZE*2))M
++@@ -174,7 +232,8 @@ for fs in $TEST_FS; do
++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
++
++ # pool doesn't exist
++- ssm create --fstype $fs -s $((DEV_SIZE/2))m -n randvol $dev4 $mnt2
+++ ssm create --fstype $fs -s $((DEV_SIZE/2))m -n randvol $dev4
+++ ssm mount $SSM_LVM_DEFAULT_POOL/randvol $mnt2
++ size=`align_size_up $((DEV_SIZE/2))`
++ check lv_field $SSM_LVM_DEFAULT_POOL/randvol lv_size $size.00m
++ check mountpoint $SSM_LVM_DEFAULT_POOL-randvol $mnt2
++@@ -282,4 +341,8 @@ not ssm create
++ ssm add $TEST_DEVS
++ not ssm create -p $pool1
++ not ssm create -r 0 -I 16 -i 3 $dev1 $dev2
+++not ssm create -s-20M
+++not ssm create -s+20M
+++not ssm create --size 25.8.1991
+++not ssm create --size linux
++ ssm -f remove --all
++diff --git a/tests/bashtests/004-lvm-resize.sh b/tests/bashtests/004-lvm-resize.sh
++index f430851..c863f36 100755
++--- a/tests/bashtests/004-lvm-resize.sh
+++++ b/tests/bashtests/004-lvm-resize.sh
++@@ -48,20 +48,79 @@ TEST_MNT=$TESTDIR/mnt
++
++ _test_resize()
++ {
++- size=$((TEST_MAX_SIZE/2))
++- echo 'y' | ssm -f resize --size ${size}M ${DM_DEV_DIR}/$DEFAULT_VOLUME
+++ # Test with no device
+++ # Test size increase
+++ size=$DEV_SIZE
+++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ size=$((DEV_SIZE + 12))
+++ ssm resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1
+++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ size=$((size + 3))
+++ ssm resize -s +3M $SSM_LVM_DEFAULT_POOL/$lvol1
++ size=$(align_size_up $size)
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ # Test size decrese
+++ if [ "$fs" != "xfs" ]; then
+++ size=$((size - 8))
+++ ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1
+++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ size=$((size - 7))
+++ ssm -f resize -s-7M $SSM_LVM_DEFAULT_POOL/$lvol1
+++ size=$(align_size_up $size)
+++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ # Test with devcie belongs to no pool
+++ # size decrease
+++ size=$((size - 12))
+++ ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev4
+++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
+++ check vg_field $pool1 pv_count 3
+++ fi
+++ # If the volume is already of the given size ssm will attempt to resize
+++ # file system to cover the whole device. Note that we do not check for
+++ # the file system size because it's not really necessary. So this would
+++ # fail if the file system is present. This might change in future, so
+++ # comment it out for now.
+++ # size doesn't change
+++ #not ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev4
+++ #check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ #check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
+++ #check vg_field $pool1 pv_count 3
+++ # size increase
+++ size=$((size + 12))
+++ ssm resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev4
+++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
+++ check vg_field $pool1 pv_count 3
++
++- # xfs does not support shrinking (xfs only grows big!! :))
+++ # Test with device belongs to other pool
+++ # size decrease
++ if [ "$fs" != "xfs" ]; then
++- ssm -f -v resize -s-$(($TEST_MAX_SIZE/4))M $DEFAULT_VOLUME
++- size=$(align_size_up $(($size-($TEST_MAX_SIZE/4))))
+++ size=$((size - 12))
+++ ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev6
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
+++ check vg_field $pool1 pv_count 3
++ fi
++- echo 'y' | ssm -f resize --size +$(($TEST_MAX_SIZE/5))M $DEFAULT_VOLUME
++- size=$(align_size_up $(($size+($TEST_MAX_SIZE/5))))
+++ # size doesn't change
+++ #not ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev6
+++ #check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ #check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
+++ #check vg_field $pool1 pv_count 3
+++ # size increase
+++ size=$((size + 12))
+++ ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev6
+++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
+++ check vg_field $pool1 pv_count 3
+++
+++ # when resize to excessive amount
+++ size=$((DEV_SIZE*4))
+++ not ssm resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1
+++ ssm resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev8 $dev9
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
+++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 5
+++
++ }
++
++ ssm add $TEST_DEVS
++@@ -100,46 +159,37 @@ ssm -f remove $SSM_LVM_DEFAULT_POOL
++ ssm create --size $((DEV_SIZE/2))M $dev1
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 1
++ ssm resize --size +$((DEV_SIZE/3))M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2
++-check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 2
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 1
++ ssm -f resize -s-$((DEV_SIZE/3))M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2 $dev3
++ ssm resize --size +$((DEV_SIZE/3))M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2 $dev3
++-check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 3
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 1
++ ssm -f resize -s-$((DEV_SIZE/3))M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2 $dev3
++-check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 3
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 1
++ ssm resize --size +${DEV_SIZE}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2 $dev3
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 3
++ ssm -f remove $SSM_LVM_DEFAULT_POOL
++
+++
+++ssm add $dev{1,2,3}
+++ssm create -s ${DEV_SIZE}M
+++ssm add -p $pool1 $dev{5,6,7}
+++_test_resize
+++ssm -f remove $SSM_LVM_DEFAULT_POOL
+++ssm -f remove $pool1
+++
+++
++ [ ! -d $TEST_MNT ] && mkdir $TEST_MNT &> /dev/null
++ for fs in $TEST_FS; do
++- # umounted test
++- ssm add $TEST_DEVS
++- size=$((TEST_MAX_SIZE/4))
++- ssm create --fs $fs --size ${size}M $TEST_DEVS
++-
+++ ssm add $dev{1,2,3}
+++ ssm create -s ${DEV_SIZE}M --fs $fs
+++ ssm -f check $DEFAULT_VOLUME
+++ ssm add -p $pool1 $dev{5,6,7}
++ _test_resize
++ ssm -f check $DEFAULT_VOLUME
++ ssm -f remove $SSM_LVM_DEFAULT_POOL
++-
++- echo $fs
++- if [ $fs == 'ext2' ]; then
++- continue
++- fi
++-
++- # Disable this for now, since fsadm does not handle -f and -y correctly
++- # mounted test
++- #ssm add $TEST_DEVS
++- size=$((TEST_MAX_SIZE/4))
++- #ssm create --fs $fs --size ${size}M $TEST_DEVS
++-
++- #mount ${DM_DEV_DIR}/$DEFAULT_VOLUME $TEST_MNT
++-
++- #_test_resize
++-
++- #umount $TEST_MNT
++- #ssm -f check $DEFAULT_VOLUME
++- #ssm -f remove $SSM_LVM_DEFAULT_POOL
+++ ssm -f remove $pool1
++ done
+++# There should not be anything to remove
++ not ssm -f remove --all
++
++ ssm create $dev1
++diff --git a/tests/bashtests/007-btrfs-create.sh b/tests/bashtests/007-btrfs-create.sh
++index 1ea27df..c971a76 100755
++--- a/tests/bashtests/007-btrfs-create.sh
+++++ b/tests/bashtests/007-btrfs-create.sh
++@@ -88,6 +88,7 @@ ssm -f remove $SSM_BTRFS_DEFAULT_POOL
++
++ # Create raid 10 volume with just one device
++ ssm create -r 10 $dev1 $dev2 $dev3 $dev4
+++ssm check $SSM_BTRFS_DEFAULT_POOL
++ not ssm create $dev1 -p $pool1
++ ssm -f remove $SSM_BTRFS_DEFAULT_POOL
++
++diff --git a/tests/bashtests/011-lvm-list.sh b/tests/bashtests/011-lvm-list.sh
++index 66567a1..73cea90 100755
++--- a/tests/bashtests/011-lvm-list.sh
+++++ b/tests/bashtests/011-lvm-list.sh
++@@ -1,6 +1,6 @@
++ #!/bin/bash
++ #
++-# (C)2012 Red Hat, Inc., Tom Marek <tmarek at redhat.com>
+++# (C)2013 Red Hat, Inc., Jimmy Pan <jipan at redhat.com>
++ #
++ # This program is free software: you can redistribute it and/or modify
++ # it under the terms of the GNU General Public License as published by
++@@ -21,9 +21,10 @@ export test_description='Check whether list command prints correct values for lv
++ . lib/test
++
++ DEV_COUNT=10
++-DEV_SIZE=100
+++DEV_SIZE=128
++ TEST_MAX_SIZE=$(($DEV_COUNT*$DEV_SIZE))
++ aux prepare_devs $DEV_COUNT $DEV_SIZE
+++aux prepare_mnts 10
++ TEST_DEVS=$(cat DEVICES)
++ export LVOL_PREFIX="lvol"
++ export SSM_DEFAULT_BACKEND='lvm'
++@@ -33,8 +34,14 @@ export SSM_NONINTERACTIVE='1'
++ snap1="snap1"
++ snap2="snap2"
++
+++lvol1=${LVOL_PREFIX}001
+++lvol2=${LVOL_PREFIX}002
+++lvol3=${LVOL_PREFIX}003
+++
+++pool0=$vg1
++ pool1=$vg2
++ pool2=$vg3
+++pool3=$vg4
++
++ TEST_FS=
++ which mkfs.ext2 && TEST_FS+="ext2 "
++@@ -45,28 +52,262 @@ which mkfs.xfs && TEST_FS+="xfs"
++ TEST_MNT=$TESTDIR/mnt
++ [ ! -d $TEST_MNT ] && mkdir $TEST_MNT &> /dev/null
++
++-##LVM
++-# Check devices
++-ssm add $TEST_DEVS
++-ssm_output=$(ssm list dev)
++-for device in ${TEST_DEVS}; do
++- check list_table "$ssm_output" $device 96.00MB 0.00KB 96.00MB $SSM_LVM_DEFAULT_POOL
+++# Prepare pools and volumes
+++
+++vol1=volsf
+++vol2=volss
+++vol3=volmf
+++vol4=volms1
+++vol5=volms2
+++vol6=volms3
+++maxvolsz=$((DEV_SIZE-4))
+++size1=$maxvolsz
+++size2=$((DEV_SIZE/2))
+++size3=$((maxvolsz*2))
+++size4=$((DEV_SIZE/2))
+++size5=$((DEV_SIZE*2))
+++size6=$((DEV_SIZE/4))
+++size4s=$((size4-20))
+++size2r=$((size2-4))
+++size5r=$((size5+16))
+++
+++
+++# Test without a filesystem
+++ssm -f create -n $vol1 $dev1
+++ssm create -n $vol2 -p $pool1 -s ${size2}M $dev2
+++ssm create -n $vol3 -p $pool2 $dev3 $dev4
+++ssm add -p $pool3 $dev{5,6,7,8}
+++ssm create -p $pool3 -s ${size4}m -n $vol4
+++ssm create -p $pool3 -s ${size5}m -n $vol5
+++ssm create -p $pool3 -s ${size6}m -n $vol6
+++
+++# We shouldn't see ssm list fs here
+++test `ssm list fs | wc -l` -le 1
+++# Check vol, dev, pool, resized vol, and snapshot
+++output=`ssm list vol`
+++check list_table "$output" $pool0/$vol1 $pool0 $((size1)).00MB linear
+++check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB linear
+++check list_table "$output" $pool2/$vol3 $pool2 $((size3)).00MB linear
+++check list_table "$output" $pool3/$vol4 $pool3 $((size4)).00MB linear
+++check list_table "$output" $pool3/$vol5 $pool3 $((size5)).00MB linear
+++check list_table "$output" $pool3/$vol6 $pool3 $((size6)).00MB linear
+++output=`ssm list dev`
+++check list_table "$output" $dev1 0.00KB $((size1)).00MB 124.00MB $pool0
+++check list_table "$output" $dev2 $((maxvolsz-size2)).00MB $((size2)).00MB 124.00MB $pool1
+++check list_table "$output" $dev3 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
+++check list_table "$output" $dev4 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
+++check list_table "$output" $dev5 none none 124.00MB $pool3
+++check list_table "$output" $dev6 none none 124.00MB $pool3
+++check list_table "$output" $dev7 none none 124.00MB $pool3
+++check list_table "$output" $dev8 none none 124.00MB $pool3
+++output=`ssm list pool`
+++check list_table "$output" $pool0 lvm 1 0.00KB $((size1)).00MB $((maxvolsz)).00MB
+++check list_table "$output" $pool1 lvm 1 $((maxvolsz-size2)).00MB $((size2)).00MB $((maxvolsz)).00MB
+++check list_table "$output" $pool2 lvm 2 0.00KB $((size3)).00MB $((maxvolsz*2)).00MB
+++check list_table "$output" $pool3 lvm 4 $((maxvolsz*4-size4-size5-size6)).00MB $((size4+size5+size6)).00MB $((maxvolsz*4)).00MB
+++
+++# Check ssm vol after resize
+++ssm -f resize $pool1/$vol2 -s ${size2r}M
+++ssm resize -s ${size5r}m $pool3/$vol5
+++output=`ssm list vol`
+++check list_table "$output" $pool1/$vol2 $pool1 $size2r.00MB linear
+++check list_table "$output" $pool3/$vol5 $pool3 $((size5r)).00MB linear
+++
+++ssm snapshot $pool3/$vol4 -n snap1
+++ssm snapshot $pool3/$vol4 -s ${size4s}m -n snap2
+++output=`ssm list snap`
+++check list_table "$output" $pool3/snap1 $vol4 $pool3 none none linear
+++check list_table "$output" $pool3/snap2 $vol4 $pool3 $size4s.00MB none linear
+++
+++
+++ssm -f remove -a
+++
+++
+++# Test with filesystem
+++for fs in $TEST_FS ; do
+++ ssm -f create -n $vol1 $dev1 --fs $fs
+++ ssm create -n $vol2 -p $pool1 -s ${size2}M $dev2 --fs $fs
+++ ssm create -n $vol3 -p $pool2 $dev3 $dev4 --fs $fs
+++ ssm add -p $pool3 $dev{5,6,7,8}
+++ ssm create -p $pool3 -s ${size4}m -n $vol4 --fs $fs
+++ ssm create -p $pool3 -s ${size5}m -n $vol5 --fs $fs
+++ ssm create -p $pool3 -s ${size6}m -n $vol6 --fs $fs
+++
+++ # Check fs, vol, dev, pool, resized vol, and snapshot
+++ output=`ssm list fs`
+++ # xfs has some strange used size, so we don't check it
+++ if [ $fs == xfs ] ; then
+++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs none none linear
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs none none linear
+++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs none none linear
+++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs none none linear
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs none none linear
+++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs none none linear
+++ else
+++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs $size1.00MB none linear
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs $size2.00MB none linear
+++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs $size3.00MB none linear
+++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs $size4.00MB none linear
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs $size5.00MB none linear
+++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs $size6.00MB none linear
+++ fi
+++
+++ output=`ssm list vol`
+++ # xfs has some strange used size, so we don't check it
+++ if [ $fs == xfs ] ; then
+++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs none none linear
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs none none linear
+++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs none none linear
+++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs none none linear
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs none none linear
+++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs none none linear
+++ else
+++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs $size1.00MB none linear
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs $size2.00MB none linear
+++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs $size3.00MB none linear
+++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs $size4.00MB none linear
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs $size5.00MB none linear
+++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs $size6.00MB none linear
+++ fi
+++
+++ output=`ssm list dev`
+++ check list_table "$output" $dev1 0.00KB $((size1)).00MB 124.00MB $pool0
+++ check list_table "$output" $dev2 $((maxvolsz-size2)).00MB $((size2)).00MB 124.00MB $pool1
+++ check list_table "$output" $dev3 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
+++ check list_table "$output" $dev4 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
+++ check list_table "$output" $dev5 none none 124.00MB $pool3
+++ check list_table "$output" $dev6 none none 124.00MB $pool3
+++ check list_table "$output" $dev7 none none 124.00MB $pool3
+++ check list_table "$output" $dev8 none none 124.00MB $pool3
+++ output=`ssm list pool`
+++ check list_table "$output" $pool0 lvm 1 0.00KB $((size1)).00MB $((maxvolsz)).00MB
+++ check list_table "$output" $pool1 lvm 1 $((maxvolsz-size2)).00MB $((size2)).00MB $((maxvolsz)).00MB
+++ check list_table "$output" $pool2 lvm 2 0.00KB $((size3)).00MB $((maxvolsz*2)).00MB
+++ check list_table "$output" $pool3 lvm 4 $((maxvolsz*4-size4-size5-size6)).00MB $((size4+size5+size6)).00MB $((maxvolsz*4)).00MB
+++
+++ # Check ssm vol after resize
+++ # xfs size cannot reduce
+++ if [ "$fs" != xfs ] ; then
+++ ssm -f resize $pool1/$vol2 -s ${size2r}M
+++ fi
+++ ssm resize -s ${size5r}m $pool3/$vol5
+++
+++ output=`ssm list vol`
+++ # xfs has some strange used size, so we don't check it
+++ if [ "$fs" != xfs ] ; then
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2r.00MB $fs $size2r none linear
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5r.00MB $fs $size5r none linear
+++ else
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5r.00MB $fs none none linear
+++ fi
+++
+++ ssm snapshot $pool3/$vol4 -n snap1
+++ ssm snapshot $pool3/$vol4 -s ${size4s}m -n snap2
+++
+++ output=`ssm list snap`
+++ check list_table "$output" $pool3/snap1 $vol4 $pool3 none none linear
+++ check list_table "$output" $pool3/snap2 $vol4 $pool3 $size4s.00MB none linear
+++
+++ ssm -f remove -a
+++
++ done
++
++-# Check pools
++-check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL $SSM_DEFAULT_BACKEND $DEV_COUNT none none 960.00MB
++-ssm -f remove --all
+++# Test with a mountpoint
+++for fs in $TEST_FS ; do
+++ ssm -f create -n $vol1 --fs $fs $dev1 $mnt1
+++ ssm create -n $vol2 -p $pool1 -s ${size2}M --fs $fs $dev2 $mnt2
+++ ssm create -n $vol3 -p $pool2 --fs $fs $dev3 $dev4 $mnt3
+++ ssm add -p $pool3 $dev{5,6,7,8}
+++ ssm create -p $pool3 -s ${size4}m -n $vol4 --fs $fs $mnt4
+++ ssm create -p $pool3 -s ${size5}m -n $vol5 --fs $fs $mnt5
+++ ssm create -p $pool3 -s ${size6}m -n $vol6 --fs $fs $mnt6
++
++-# create multiple pools
++-ssm create --pool $pool1 $dev1 $dev2 $dev3 $dev4
++-ssm create --pool $pool2 $dev5 $dev6 $dev7 $dev8
++-ssm_output=$(ssm list pool)
++-check list_table "$ssm_output" $pool1 lvm 4 none none 384.00MB
++-check list_table "$ssm_output" $pool2 lvm 4 none none 384.00MB
++-ssm -f remove --all
+++ # Check fs, vol, dev, pool, resized vol, and snapshot
+++ output=`ssm list fs`
+++ # xfs has some strange used size, so we don't check it
+++ if [ $fs == xfs ] ; then
+++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs none none linear $mnt1
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs none none linear $mnt2
+++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs none none linear $mnt3
+++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs none none linear $mnt4
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs none none linear $mnt5
+++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs none none linear $mnt6
+++ else
+++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs $size1.00MB none linear $mnt1
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs $size2.00MB none linear $mnt2
+++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs $size3.00MB none linear $mnt3
+++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs $size4.00MB none linear $mnt4
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs $size5.00MB none linear $mnt5
+++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs $size6.00MB none linear $mnt6
+++ fi
++
++-ssm add $TEST_DEVS
+++ output=`ssm list vol`
+++ # xfs has some strange used size, so we don't check it
+++ if [ $fs == xfs ] ; then
+++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs none none linear $mnt1
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs none none linear $mnt2
+++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs none none linear $mnt3
+++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs none none linear $mnt4
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs none none linear $mnt5
+++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs none none linear $mnt6
+++ else
+++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs $size1.00MB none linear $mnt1
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs $size2.00MB none linear $mnt2
+++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs $size3.00MB none linear $mnt3
+++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs $size4.00MB none linear $mnt4
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs $size5.00MB none linear $mnt5
+++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs $size6.00MB none linear $mnt6
+++ fi
+++
+++ output=`ssm list dev`
+++ check list_table "$output" $dev1 0.00KB $((size1)).00MB 124.00MB $pool0
+++ check list_table "$output" $dev2 $((maxvolsz-size2)).00MB $((size2)).00MB 124.00MB $pool1
+++ check list_table "$output" $dev3 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
+++ check list_table "$output" $dev4 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
+++ check list_table "$output" $dev5 none none 124.00MB $pool3
+++ check list_table "$output" $dev6 none none 124.00MB $pool3
+++ check list_table "$output" $dev7 none none 124.00MB $pool3
+++ check list_table "$output" $dev8 none none 124.00MB $pool3
+++ output=`ssm list pool`
+++ check list_table "$output" $pool0 lvm 1 0.00KB $((size1)).00MB $((maxvolsz)).00MB
+++ check list_table "$output" $pool1 lvm 1 $((maxvolsz-size2)).00MB $((size2)).00MB $((maxvolsz)).00MB
+++ check list_table "$output" $pool2 lvm 2 0.00KB $((size3)).00MB $((maxvolsz*2)).00MB
+++ check list_table "$output" $pool3 lvm 4 $((maxvolsz*4-size4-size5-size6)).00MB $((size4+size5+size6)).00MB $((maxvolsz*4)).00MB
+++
+++ # Check ssm vol after resize
+++ # xfs size cannot reduce
+++ if [ "$fs" != xfs ] ; then
+++ umount $mnt2
+++ ssm -f resize $pool1/$vol2 -s ${size2r}M
+++ fi
+++ ssm resize -s ${size5r}m $pool3/$vol5
+++
+++ output=`ssm list vol`
+++ # xfs has some strange used size, so we don't check it
+++ if [ "$fs" != xfs ] ; then
+++ check list_table "$output" $pool1/$vol2 $pool1 $size2r.00MB $fs $size2r none linear
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5r.00MB $fs $size5r none linear
+++ else
+++ check list_table "$output" $pool3/$vol5 $pool3 $size5r.00MB $fs none none linear
+++ fi
++
+++ ssm snapshot $pool3/$vol4 -n snap1
+++ ssm snapshot $pool3/$vol4 -s ${size4s}m -n snap2
+++
+++ output=`ssm list snap`
+++ check list_table "$output" $pool3/snap1 $vol4 $pool3 none none linear
+++ check list_table "$output" $pool3/snap2 $vol4 $pool3 $size4s.00MB none linear
+++
+++ for i in {1..6} ; do
+++ mntdir=`eval echo '$mnt'$i`
+++ umount $mntdir || true
+++ done
+++
+++ ssm -f remove -a
+++
+++done
+++
+++ssm add $TEST_DEVS
++ # Check LVM volume listings with various fs
++ for fs in $TEST_FS; do
++ name="${fs}vol"
++@@ -85,30 +326,5 @@ for fs in $TEST_FS; do
++ done
++ ssm -f remove $SSM_LVM_DEFAULT_POOL
++
++-# Check lvm snapshot
++-lvol1=${LVOL_PREFIX}001
++-# Create volume with all devices at once
++-size=$(($DEV_SIZE*6))
++-snap_size1=$(($DEV_SIZE))
++-snap_size2=$(($size/5))
++-ssm create --size ${size}M $TEST_DEVS
++-ssm snapshot --name $snap1 --size ${snap_size1}M $SSM_LVM_DEFAULT_POOL/$lvol1
++-ssm snapshot --name $snap2 --size ${snap_size2}M $SSM_LVM_DEFAULT_POOL/$lvol1
++-ssm_output=$(ssm list snap)
++-check list_table "$ssm_output" $snap1 $lvol1 $SSM_LVM_DEFAULT_POOL ${snap_size1}.00MB 0.00KB linear
++-check list_table "$ssm_output" $snap2 $lvol1 $SSM_LVM_DEFAULT_POOL ${snap_size2}.00MB 0.00KB linear
++-ssm -f remove --all
++-
++-# Snapshot of the volumes in defferent pools
++-ssm create --pool $pool1 $dev1 $dev2
++-ssm add $dev3 $dev4 --pool $pool1
++-ssm create --pool $pool2 $dev5 $dev6
++-ssm add $dev7 $dev8 --pool $pool2
++-ssm snapshot --name $snap1 $pool1/$lvol1
++-ssm snapshot --name $snap1 $pool2/$lvol1
++-ssm_output=$(ssm list snap)
++-check list_table "$ssm_output" "$pool1/$snap1" $lvol1 $pool1 40.00MB 0.00KB linear
++-check list_table "$ssm_output" "$pool2/$snap1" $lvol1 $pool2 40.00MB 0.00KB linear
++-ssm -f remove --all
++-
++-# all_done!!!
+++# Some situation should fail
+++not ssm list wrong_type
++diff --git a/tests/bashtests/012-crypt-create.sh b/tests/bashtests/012-crypt-create.sh
++new file mode 100644
++index 0000000..8a5fddb
++--- /dev/null
+++++ b/tests/bashtests/012-crypt-create.sh
++@@ -0,0 +1,143 @@
+++#!/bin/bash
+++#
+++# (C)2013 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+++#
+++# This program is free software: you can redistribute it and/or modify
+++# it under the terms of the GNU General Public License as published by
+++# the Free Software Foundation, either version 2 of the License, or
+++# (at your option) any later version.
+++#
+++# This program is distributed in the hope that it will be useful,
+++# but WITHOUT ANY WARRANTY; without even the implied warranty of
+++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+++# GNU General Public License for more details.
+++#
+++# You should have received a copy of the GNU General Public License
+++# along with this program. If not, see <http://www.gnu.org/licenses/>.
+++
+++export test_name='012-crypt-create'
+++test_description='Exercise crypt create'
+++
+++. lib/test
+++
+++DEV_COUNT=10
+++DEV_SIZE=100
+++TEST_MAX_SIZE=$(($DEV_COUNT*$DEV_SIZE))
+++aux prepare_devs $DEV_COUNT $DEV_SIZE
+++aux prepare_mnts 4
+++TEST_DEVS=$(cat DEVICES)
+++export SSM_DEFAULT_BACKEND='crypt'
+++export SSM_CRYPT_DEFAULT_POOL=$vg1
+++export CRYPT_VOL_PREFIX="${SSM_PREFIX_FILTER}enc"
+++export SSM_NONINTERACTIVE='1'
+++export SSM_CRYPT_DEFAULT_VOL_PREFIX=$CRYPT_VOL_PREFIX
+++crypt_vol1=${CRYPT_VOL_PREFIX}001
+++crypt_vol2=${CRYPT_VOL_PREFIX}002
+++crypt_vol3=${CRYPT_VOL_PREFIX}003
+++crypt_vol4=${CRYPT_VOL_PREFIX}004
+++passwd="cai0ohMo8M"
+++
+++pool1=$vg2
+++pool2=$vg3
+++DEV="/dev/mapper"
+++
+++# cryptsetup will ask for password. If luks extension is used
+++# it will ask when creating header and then when opening the device
+++pass() {
+++ echo -e "${passwd}\n${passwd}"
+++}
+++
+++
+++# Create encrypted volume
+++pass | ssm create $dev1
+++check crypt_vol_field $crypt_vol1 type LUKS1
+++check crypt_vol_field $crypt_vol1 device $dev1
+++check list_table "$(ssm list vol)" $crypt_vol1 $SSM_CRYPT_DEFAULT_POOL none crypt
+++
+++pass | ssm create $dev2 -e
+++check crypt_vol_field $crypt_vol2 type LUKS1
+++check crypt_vol_field $crypt_vol2 device $dev2
+++check list_table "$(ssm list vol)" $crypt_vol2 $SSM_CRYPT_DEFAULT_POOL none crypt
+++
+++pass | ssm create -e luks $dev3
+++check crypt_vol_field $crypt_vol3 type LUKS1
+++check crypt_vol_field $crypt_vol3 device $dev3
+++check list_table "$(ssm list vol)" $crypt_vol3 $SSM_CRYPT_DEFAULT_POOL none crypt
+++
+++pass | ssm create --fs ext4 -e plain $dev4 $mnt1
+++check mountpoint $crypt_vol4 $mnt1
+++check crypt_vol_field $crypt_vol4 type PLAIN
+++check crypt_vol_field $crypt_vol4 device $dev4
+++check list_table "$(ssm list vol)" $crypt_vol4 $SSM_CRYPT_DEFAULT_POOL none ext4 none none crypt
+++ssm list
+++umount $mnt1
+++ssm -f remove ${DEV}/$crypt_vol1
+++
+++pass | ssm create --fs ext3 -s 50M -e plain $dev1 $mnt1
+++check mountpoint $crypt_vol1 $mnt1
+++check crypt_vol_field $crypt_vol1 type PLAIN
+++check crypt_vol_field $crypt_vol1 device $dev1
+++check crypt_vol_field $crypt_vol1 size 102400
+++check list_table "$(ssm list vol)" $crypt_vol1 $SSM_CRYPT_DEFAULT_POOL none ext3 none none crypt
+++umount $mnt1
+++
+++ssm remove ${DEV}/$crypt_vol1 ${DEV}/$crypt_vol3 ${DEV}/$crypt_vol2 ${DEV}/$crypt_vol4
+++
+++# Try non existing extension
+++not ssm create -e enigma $dev1
+++
+++# Create encrypted lvm volume
+++export SSM_LVM_DEFAULT_POOL=${vg1}_lvm
+++export LVOL_PREFIX="lvol"
+++lvol1=${LVOL_PREFIX}001
+++lvol2=${LVOL_PREFIX}002
+++lvol3=${LVOL_PREFIX}003
+++lvol4=${LVOL_PREFIX}004
+++export SSM_DEFAULT_BACKEND='lvm'
+++
+++pass | ssm create --fs xfs $dev1 $dev2 $mnt1 -e
+++check mountpoint $crypt_vol1 $mnt1
+++check crypt_vol_field $crypt_vol1 type LUKS1
+++check crypt_vol_field $crypt_vol1 device ${SSM_LVM_DEFAULT_POOL}-$lvol1
+++check list_table "$(ssm list vol)" $crypt_vol1 $SSM_CRYPT_DEFAULT_POOL none xfs none none crypt
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL none linear
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 2
+++
+++pass | ssm create -r0 $dev3 $dev4 -e plain
+++check crypt_vol_field $crypt_vol2 type PLAIN
+++check crypt_vol_field $crypt_vol2 device ${SSM_LVM_DEFAULT_POOL}-$lvol2
+++check list_table "$(ssm list vol)" $crypt_vol2 $SSM_CRYPT_DEFAULT_POOL none crypt
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol2 $SSM_LVM_DEFAULT_POOL none striped
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol2 pv_count 4
+++
+++pass | ssm create $dev5 -e luks
+++check crypt_vol_field $crypt_vol3 type LUKS1
+++check crypt_vol_field $crypt_vol3 device ${SSM_LVM_DEFAULT_POOL}-$lvol3
+++check list_table "$(ssm list vol)" $crypt_vol3 $SSM_CRYPT_DEFAULT_POOL none crypt
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol3 $SSM_LVM_DEFAULT_POOL none linear
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol3 pv_count 5
+++
+++pass | ssm create -e plain --fs xfs -r10 -s ${DEV_SIZE}M $dev6 $dev7 $dev8 $dev9 $mnt2
+++check mountpoint $crypt_vol4 $mnt2
+++check crypt_vol_field $crypt_vol4 type PLAIN
+++check crypt_vol_field $crypt_vol4 device ${SSM_LVM_DEFAULT_POOL}-$lvol4
+++check list_table "$(ssm list vol)" $crypt_vol4 $SSM_CRYPT_DEFAULT_POOL 104.00M xfs none none crypt
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol4 $SSM_LVM_DEFAULT_POOL 104.00M raid10
+++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol4 pv_count 9
+++
+++ssm list
+++umount $mnt1
+++umount $mnt2
+++ssm -f remove ${DEV}/$crypt_vol1 ${DEV}/$crypt_vol3 ${DEV}/$crypt_vol2 ${DEV}/$crypt_vol4
+++ssm -f remove $SSM_LVM_DEFAULT_POOL
+++
+++ssm create $dev1 $dev2
+++ssm list
+++pass | ssm -b crypt create $DM_DEV_DIR/$SSM_LVM_DEFAULT_POOL/$lvol1
+++check crypt_vol_field $crypt_vol1 type LUKS1
+++check crypt_vol_field $crypt_vol1 device ${SSM_LVM_DEFAULT_POOL}-$lvol1
+++check list_table "$(ssm list vol)" $crypt_vol1 $SSM_CRYPT_DEFAULT_POOL none crypt
+++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL none linear
+++
+++ssm remove ${DEV}/$crypt_vol1
+++ssm -f remove $SSM_LVM_DEFAULT_POOL
++diff --git a/tests/bashtests/013-lvm-check.sh b/tests/bashtests/013-lvm-check.sh
++new file mode 100644
++index 0000000..11a70cb
++--- /dev/null
+++++ b/tests/bashtests/013-lvm-check.sh
++@@ -0,0 +1,130 @@
+++#!/bin/bash
+++#
+++# (C)2013 Red Hat, Inc., Jimmy Pan <jipan at redhat.com>
+++#
+++# This program is free software: you can redistribute it and/or modify
+++# it under the terms of the GNU General Public License as published by
+++# the Free Software Foundation, either version 2 of the License, or
+++# (at your option) any later version.
+++#
+++# This program is distributed in the hope that it will be useful,
+++# but WITHOUT ANY WARRANTY; without even the implied warranty of
+++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+++# GNU General Public License for more details.
+++#
+++# You should have received a copy of the GNU General Public License
+++# along with this program. If not, see <http://www.gnu.org/licenses/>.
+++
+++
+++export test_name='013-lvm-check'
+++export test_description='Exercise ssm lvm check'
+++
+++. lib/test
+++
+++DEV_COUNT=10
+++DEV_SIZE=100
+++aux prepare_devs $DEV_COUNT $DEV_SIZE
+++TEST_DEVS=$(cat DEVICES)
+++export SSM_DEFAULT_BACKEND='lvm'
+++export LVOL_PREFIX="lvol"
+++export SSM_LVM_DEFAULT_POOL=$vg1
+++export SSM_NONINTERACTIVE='1'
+++lvol1=${LVOL_PREFIX}001
+++lvol2=${LVOL_PREFIX}002
+++lvol3=${LVOL_PREFIX}003
+++
+++
+++pool1=$vg2
+++pool2=$vg3
+++
+++TEST_FS=
+++which mkfs.ext2 && grep -E "^\sext[234]$" /proc/filesystems && TEST_FS+="ext2 "
+++which mkfs.ext3 && grep -E "^\sext[34]$" /proc/filesystems && TEST_FS+="ext3 "
+++which mkfs.ext4 && grep -E "^\sext4$" /proc/filesystems && TEST_FS+="ext4 "
+++which mkfs.xfs && grep -E "^\sxfs$" /proc/filesystems && TEST_FS+="xfs"
+++
+++filesystem_check()
+++{
+++ if [ $# -lt 3 ] ; then
+++ echo Usage: filesystem_check pool volume filesystem
+++ exit 1
+++ fi
+++ local pool=$1
+++ local vol=$2
+++ local fs=$3
+++ local name=$pool/$vol
+++ local path=`lvs --noheadings -o lv_path $name`
+++ if [[ $fs == ext[234] ]] ; then
+++ e2fsck -n $path
+++ elif [[ $fs == xfs ]] ; then
+++ xfs_repair -n $path
+++ else
+++ echo Invalid fs type
+++ exit 1
+++ fi
+++}
+++
+++
+++# No filesystem should fail
+++ssm create $dev1
+++not ssm check $SSM_LVM_DEFAULT_POOL/$lvol1
+++ssm -f remove -a
+++
+++# Loop for different filesystems
+++for fs in $TEST_FS ; do
+++ # Test check of a volume with one device
+++ ssm create --fs $fs -p $SSM_LVM_DEFAULT_POOL $dev1
+++ ret1=0
+++ filesystem_check $SSM_LVM_DEFAULT_POOL $lvol1 $fs || ret1=$?
+++ ret2=0
+++ ssm check $SSM_LVM_DEFAULT_POOL/$lvol1 || ret2=$?
+++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
+++ ret2=0
+++ ssm check `lvs --noheadings -o lv_path $SSM_LVM_DEFAULT_POOL/$lvol1` || ret2=$?
+++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
+++
+++ # Test check of a volume with one device with one device has specified
+++ # size and on two devices
+++ ssm create -p $pool1 --fs $fs -s $((DEV_SIZE+20))m $dev{2,3}
+++ ret1=0
+++ filesystem_check $pool1 $lvol1 $fs || ret1=$?
+++ ret2=0
+++ ssm check $pool1/$lvol1 | ret2=$?
+++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
+++ ret2=0
+++ ssm check `lvs --noheadings -o lv_path $pool1/$lvol1` || ret2=$?
+++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
+++
+++ # Test check of volumes from the same pools
+++ ssm create -p $pool2 --fs $fs $dev{4,5,6}
+++ ssm create --fs $fs $dev{7,8} -p $pool2
+++ ret1=0
+++ filesystem_check $pool2 $lvol1 $fs || ret1=$?
+++ ret2=0
+++ ssm check $pool2/$lvol1 || ret2=$?
+++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
+++ ret2=0
+++ ssm check `lvs --noheadings -o lv_path $pool2/$lvol1` || ret2=$?
+++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
+++ ret1=0
+++ filesystem_check $pool2 $lvol2 $fs || ret1=$?
+++ ret2=0
+++ ssm check $pool2/$lvol2 || ret2=$?
+++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
+++
+++ # Test check of volumes from different pools at once
+++ ret2=0
+++ ssm check `lvs --noheadings -o lv_path $pool2/$lvol2` || ret2=$?
+++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
+++ vols="$SSM_LVM_DEFAULT_POOL/$lvol1 $pool1/$lvol1 $pool2/$lvol1 $pool2/$lvol2"
+++ ssm check $vols | not grep -i fail
+++ ssm check `lvs --noheadings -o lv_path $vols` | not grep -i fail
+++
+++ # Check invalid volume name should fail
+++ not ssm check $pool2/$lvol2"nsdf"
+++ ssm -f remove -a
+++done
+++
+++# Some should fail
+++not ssm check
+++not ssm check non_existing
++diff --git a/tests/bashtests/lib/aux.sh b/tests/bashtests/lib/aux.sh
++index 6add5c3..11a101f 100644
++--- a/tests/bashtests/lib/aux.sh
+++++ b/tests/bashtests/lib/aux.sh
++@@ -347,6 +347,7 @@ activation/polling_interval = 0
++ activation/snapshot_autoextend_percent = 50
++ activation/snapshot_autoextend_threshold = 50
++ activation/monitoring = 0
+++global/use_lvmetad = 0
++ EOF
++ }
++
++diff --git a/tests/bashtests/lib/check.sh b/tests/bashtests/lib/check.sh
++index 31d8c2d..9a8f853 100644
++--- a/tests/bashtests/lib/check.sh
+++++ b/tests/bashtests/lib/check.sh
++@@ -292,6 +292,35 @@ pvlv_counts()
++ vg_field $local_vg snap_count $num_snaps
++ }
++
+++crypt_vol_field()
+++{
+++ data=$(cryptsetup status $1 2> /dev/null | grep $2 | sed -e 's/^[ \t]*//' || true)
+++
+++ expected=$3
+++ case $2 in
+++ "type")
+++ actual=$(echo ${data##*type:} | sed -e 's/^[ \t]*//') ;;
+++ "device")
+++ actual=$(echo ${data##*device:} | sed -e 's/^[ \t]*//')
+++ actual=$(basename $actual)
+++ expected=$(basename $expected)
+++ ;;
+++ "size")
+++ actual=$(echo ${data##*size:} | sed -e 's/^[ \t]*//')
+++ actual=${actual%% sectors}
+++ ;;
+++ *)
+++ echo "Unknown field $2"
+++ exit 1
+++ ;;
+++ esac
+++
+++ test "$actual" = "$expected" || {
+++ echo "crypt_vol_field: volume=$1, field=$2, actual=$actual, expected=$expected"
+++ exit 1
+++ }
+++}
+++
++ btrfs_fs_field()
++ {
++ lines=$(btrfs filesystem show 2> /dev/null | grep -A 1 "'$1'" || true)
++diff --git a/tests/bashtests/set.sh b/tests/bashtests/set.sh
++index 1306a4e..d2fcf7d 100755
++--- a/tests/bashtests/set.sh
+++++ b/tests/bashtests/set.sh
++@@ -15,6 +15,7 @@
++ # You should have received a copy of the GNU General Public License
++ # along with this program. If not, see <http://www.gnu.org/licenses/>.
++
+++chmod +x *.sh
++ for i in $(ls lib/*.sh); do
++ echo "$i -> ${i%%.sh}"
++ cp $i ${i%%.sh}
++diff --git a/tests/unittests/common.py b/tests/unittests/common.py
++index ec60ae2..58c5637 100644
++--- a/tests/unittests/common.py
+++++ b/tests/unittests/common.py
++@@ -74,8 +74,8 @@ class MockSystemDataSource(unittest.TestCase):
++ misc.run = self.mock_run
++ self.get_partitions_orig = misc.get_partitions
++ misc.get_partitions = self.mock_get_partitions
++- self.is_bdevice_orig = main.is_bdevice
++- main.is_bdevice = self.mock_is_bdevice
+++ self.is_bdevice_orig = misc.is_bdevice
+++ misc.is_bdevice = self.mock_is_bdevice
++ self.is_directory_orig = main.is_directory
++ main.is_directory = self.mock_is_directory
++ self.check_create_item_orig = main.StorageHandle.check_create_item
++@@ -88,6 +88,8 @@ class MockSystemDataSource(unittest.TestCase):
++ misc.check_binary = self.mock_check_binary
++ self.send_udev_event_orig = misc.send_udev_event
++ misc.send_udev_event = self.mock_send_udev_event
+++ self.get_fs_type_orig = misc.get_fs_type
+++ misc.get_fs_type = self.mock_get_fs_type
++ self.dev_data = {}
++ self.vol_data = {}
++ self.pool_data = {}
++@@ -104,25 +106,32 @@ class MockSystemDataSource(unittest.TestCase):
++ self.mount_data = {}
++ misc.run = self.run_orig
++ misc.get_partitions = self.get_partitions_orig
++- main.is_bdevice = self.is_bdevice_orig
+++ misc.is_bdevice = self.is_bdevice_orig
++ main.is_directory = self.is_directory_orig
++ misc.get_mounts = self.get_mounts_orig
++ main.StorageHandle.check_create_item = self.check_create_item_orig
++ misc.temp_mount = self.temp_mount_orig
++ misc.check_binary = self.check_binary_orig
++ misc.send_udev_event = self.send_udev_event_orig
+++ misc.get_fs_type = self.get_fs_type_orig
++ main.SSM_NONINTERACTIVE = False
++
++ def _cmdEq(self, out, index=-1):
++ self.assertEqual(self.run_data[index], out)
++
++- def _checkCmd(self, command, args, expected=None):
+++ def _cmdNotEq(self, out, index=-1):
+++ self.assertNotEqual(self.run_data[index], out)
+++
+++ def _checkCmd(self, command, args, expected=None, NotEq=False):
++ self.run_data = []
++ for case in misc.permutations(args):
++ cmd = command + " " + " ".join(case)
++ main.main(cmd)
++ if expected:
++- self._cmdEq(expected)
+++ if NotEq:
+++ self._cmdNotEq(expected)
+++ else:
+++ self._cmdEq(expected)
++
++ def mock_run(self, cmd, *args, **kwargs):
++ self.run_data.append(" ".join(cmd))
++@@ -166,7 +175,21 @@ class MockSystemDataSource(unittest.TestCase):
++ if path in self.directories:
++ self._mpoint = path
++ return
++- return main.is_bdevice(path)
+++ return misc.is_bdevice(path)
+++
+++ def mock_get_fs_type(self, device):
+++ if device in self.vol_data:
+++ if 'fstype' in self.vol_data[device]:
+++ return self.vol_data[device]['fstype']
+++ else:
+++ return None
+++ elif device in self.dev_data:
+++ if 'fstype' in self.dev_data[device]:
+++ return self.dev_data[device]['fstype']
+++ else:
+++ return None
+++ else:
+++ return None
++
++ def mock_send_udev_event(self, device, event):
++ pass
++@@ -211,6 +234,8 @@ class MockSystemDataSource(unittest.TestCase):
++ pool_data = self.pool_data[pool_name]
++ pool_free = float(pool_data['pool_free']) - vol_size
++ pool_used = float(pool_data['pool_used']) + vol_size
+++ pool_data['pool_free'] = pool_free
+++ pool_data['pool_used'] = pool_used
++ if mount:
++ self.pool_data[pool_name]['mount'] = mount
++ self._addDir(mount)
++diff --git a/tests/unittests/test_btrfs.py b/tests/unittests/test_btrfs.py
++index b755414..d1f8135 100644
++--- a/tests/unittests/test_btrfs.py
+++++ b/tests/unittests/test_btrfs.py
++@@ -30,11 +30,11 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ self._addDevice('/dev/sda', 11489037516)
++ self._addDevice('/dev/sdb', 234566451)
++ self._addDevice('/dev/sdc', 2684354560)
++- self._addDevice('/dev/sdc1', 894784853, 1)
+++ self._addDevice('/dev/sdc1', 2042177280, 1)
++ self._addDevice('/dev/sdc2', 29826161, 2)
++ self._addDevice('/dev/sdc3', 1042177280, 3)
++ self._addDevice('/dev/sdd', 11673)
++- self._addDevice('/dev/sde', 1042177280)
+++ self._addDevice('/dev/sde', 1073741824)
++ main.SSM_DEFAULT_BACKEND = 'btrfs'
++
++ self.check_new_path_orig = btrfs.BtrfsPool._check_new_path
++@@ -118,8 +118,8 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda".format(default_pool))
++ self._checkCmd("ssm create", ['-r 0', '-s 2.6T', '/dev/sda'],
++ "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda".format(default_pool))
++- self._checkCmd("ssm create", ['-r 1', '-s 512k', '/dev/sda'],
++- "mkfs.btrfs -L btrfs_pool -m raid1 -d raid1 -b 524288 -f /dev/sda".format(default_pool))
+++ self._checkCmd("ssm create", ['-r 1', '-s 512k', '/dev/sda /dev/sdb'],
+++ "mkfs.btrfs -L btrfs_pool -m raid1 -d raid1 -b 524288 -f /dev/sda /dev/sdb".format(default_pool))
++ self._checkCmd("ssm create", ['-r 10', '-s 10M', '/dev/sda'],
++ "mkfs.btrfs -L btrfs_pool -m raid10 -d raid10 -b 10485760 -f /dev/sda".format(default_pool))
++
++@@ -244,6 +244,8 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ "btrfs subvolume snapshot /mnt/mount /mnt/mount/new_snap")
++
++ def test_btrfs_resize(self):
+++ # Btrfs resize not supported for now because it simply does not work
+++ return
++ # Generate some storage data
++ self._addPool('default_pool', ['/dev/sda', '/dev/sdb'])
++ self._addPool('my_pool', ['/dev/sdc2', '/dev/sdc3'])
++@@ -265,9 +267,16 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ "btrfs filesystem resize 11723608063K /tmp/mount")
++ main.SSM_DEFAULT_BACKEND = 'btrfs'
++
++- self._cmdEq("btrfs device add /dev/sde /tmp/mount", -2)
+++ self._cmdNotEq("btrfs device add /dev/sde /tmp/mount", -2)
+++
+++ # Resize with enough space in the pool
++ self._checkCmd("ssm resize --size +1g", ['my_pool /dev/sde'],
++ "btrfs filesystem resize 1073052017K /mnt/test1")
+++ self._cmdNotEq("btrfs device add /dev/sde /mnt/test1", -2)
+++
+++ # Resize without enough space in the pool
+++ self._checkCmd("ssm resize --size +1t", ['my_pool /dev/sde'],
+++ "btrfs filesystem resize 2145745265K /mnt/test1")
++ self._cmdEq("btrfs device add /dev/sde /mnt/test1", -2)
++
++ # Shrink volume
++@@ -285,16 +294,23 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ "btrfs filesystem resize 10240K /mnt/test1")
++
++ # Set volume and add devices
++- self._checkCmd("ssm resize -s 12T default_pool /dev/sdc1 /dev/sde",
++- [], "btrfs filesystem resize 12884901888K /tmp/mount")
+++ self._checkCmd("ssm resize -s 20T default_pool /dev/sdc1 /dev/sde",
+++ [], "btrfs filesystem resize 21474836480K /tmp/mount")
+++ self.assertNotEqual(self.run_data[-2],
+++ "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++
+++ self._checkCmd("ssm resize -s 22T default_pool /dev/sdc1 /dev/sde",
+++ [], "btrfs filesystem resize 23622320128K /tmp/mount")
++ self.assertEqual(self.run_data[-2],
++ "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++- self._checkCmd("ssm resize -s 1T my_pool /dev/sdc1 /dev/sde",
++- [], "btrfs filesystem resize 1073741824K /mnt/test1")
+++
+++ self._checkCmd("ssm resize -s 3T my_pool /dev/sdc1 /dev/sde",
+++ [], "btrfs filesystem resize 3221225472K /mnt/test1")
++ self.assertEqual(self.run_data[-2],
++ "btrfs device add /dev/sdc1 /dev/sde /mnt/test1")
++- self._checkCmd("ssm resize -s 1T my_pool /dev/sde /dev/sdc2",
++- [], "btrfs filesystem resize 1073741824K /mnt/test1")
+++
+++ self._checkCmd("ssm resize -s 3T my_pool /dev/sde /dev/sdc2",
+++ [], "btrfs filesystem resize 3221225472K /mnt/test1")
++ self.assertEqual(self.run_data[-2],
++ "btrfs device add /dev/sde /mnt/test1")
++
++@@ -308,6 +324,8 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ self.assertNotEqual(self.run_data[-2],
++ "btrfs device add /dev/sdc1 /dev/sde /mnt/test1")
++
+++ self._checkCmd("ssm list",
+++ [], "btrfs filesystem resize 12247891967K /tmp/mount")
++ # Extend volume and add devices
++ self._checkCmd("ssm resize -s +500G default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 12247891967K /tmp/mount")
++diff --git a/tests/unittests/test_lvm.py b/tests/unittests/test_lvm.py
++index 6f63fae..7af8a78 100644
++--- a/tests/unittests/test_lvm.py
+++++ b/tests/unittests/test_lvm.py
++@@ -287,10 +287,16 @@ class LvmFunctionCheck(MockSystemDataSource):
++ self.assertEqual(self.run_data[-2],
++ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
++
++- # Set volume size
+++ # Set volume size with sufficient amount of space
++ self._checkCmd("ssm resize -s 10G /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
++ [], "lvm lvresize -L 10485760.0k /dev/default_pool/vol003")
++- self.assertEqual(self.run_data[-2],
+++ self.assertNotEqual(self.run_data[-2],
+++ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
+++
+++ # Set volume size without sufficient amount of space
+++ self._checkCmd("ssm resize -s 10T /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
+++ [], "lvm lvresize -L 10737418240.0k /dev/default_pool/vol003")
+++ self.assertNotEqual(self.run_data[-2],
++ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
++
++ # Extend volume and add devices
++@@ -299,9 +305,15 @@ class LvmFunctionCheck(MockSystemDataSource):
++ self.assertEqual(self.run_data[-2],
++ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
++
++- # Extend volume
+++ # Extend volume with ehough space in pool
++ self._checkCmd("ssm resize -s +10G /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
++ [], "lvm lvresize -L 10486784.0k /dev/default_pool/vol003")
+++ self.assertNotEqual(self.run_data[-2],
+++ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
+++
+++ # Extend volume without ehough space in pool
+++ self._checkCmd("ssm resize -s +20T /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
+++ [], "lvm lvresize -L 21474837504.0k /dev/default_pool/vol003")
++ self.assertEqual(self.run_data[-2],
++ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
++
++diff --git a/tests/unittests/test_ssm.py b/tests/unittests/test_ssm.py
++index 9a5d82d..e0a1d99 100644
++--- a/tests/unittests/test_ssm.py
+++++ b/tests/unittests/test_ssm.py
++@@ -365,12 +365,12 @@ class SsmFunctionCheck(MockSystemDataSource):
++ # Set volume size
++ self._checkCmd("ssm resize -s 10G /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
++ [], "vol resize /dev/default_pool/vol003 10485760.0 False")
++- self.assertEqual(self.run_data[-2],
+++ self.assertNotEqual(self.run_data[-2],
++ "pool extend default_pool /dev/sdc1 /dev/sde")
++
++ # Extend volume size with adding more devices
++- self._checkCmd("ssm resize -s +10G /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
++- [], "vol resize /dev/default_pool/vol003 10486784.0 False")
+++ self._checkCmd("ssm resize -s +12t /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
+++ [], "vol resize /dev/default_pool/vol003 12884902912.0 False")
++ self.assertEqual(self.run_data[-2],
++ "pool extend default_pool /dev/sdc1 /dev/sde")
++
++@@ -428,9 +428,9 @@ class SsmFunctionCheck(MockSystemDataSource):
++ # Number of stripes must not exceed number of devices
++ self.assertRaises(problem.GeneralError, main.main, "ssm create -r 1 -s 2.6T -I 16 -i 4 /dev/sda")
++
++- self._checkCmd("ssm create", ['-r 1', '-s 2.6T', '-I 16', '/dev/sda'],
++- "pool create {0} 2791728742.40 1 16 /dev/sda".format(main.DEFAULT_DEVICE_POOL))
++- self._cmdEq("pool new {0} /dev/sda".format(main.DEFAULT_DEVICE_POOL), -2)
+++ self._checkCmd("ssm create", ['-r 1', '-s 2.6T', '-I 16', '/dev/sda /dev/sdb'],
+++ "pool create {0} 2791728742.40 1 16 /dev/sda /dev/sdb".format(main.DEFAULT_DEVICE_POOL))
+++ self._cmdEq("pool new {0} /dev/sda /dev/sdb".format(main.DEFAULT_DEVICE_POOL), -2)
++
++ # Create volume using single device from non existent my_pool
++ self._checkCmd("ssm create", ['--pool my_pool', '/dev/sda'],
++@@ -760,13 +760,14 @@ class PoolInfo(MyInfo):
++ misc.run(cmd)
++
++ def create(self, pool, size='', name='', devs='',
++- raid=None):
+++ options=None):
+++ options = options or {}
++ if type(devs) is not list:
++ devices = [devs]
++- if raid:
++- stripes = raid['stripes']
++- stripesize = raid['stripesize']
++- level = raid['level']
+++ if 'raid' in options:
+++ stripes = options['stripes']
+++ stripesize = options['stripesize']
+++ level = options['raid']
++ else:
++ stripes = stripesize = level = ""
++ misc.run([self.f, self.v, self.y, 'pool create', pool, size, name,
+diff --git a/ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch b/ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch
+new file mode 100644
+index 0000000..6d5cf6e
+--- /dev/null
++++ b/ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch
+@@ -0,0 +1,31 @@
++From dbc8ab76b9f50048f8cf73a792cfded7c1061023 Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Wed, 15 Jan 2014 12:19:08 +0100
++Subject: [PATCH 02/10] ssm: btrfs backend: try to find the real device
++
++Currently btrfs backend is trying to use the "device" directly from the
++output. However in recent btrfs it has been changed to print out device
++names in format (/dev/mapper/whatever) so we should really use
++get_real_device() function to obtain the "real" device name.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/backends/btrfs.py | 2 +-
++ 1 file changed, 1 insertion(+), 1 deletion(-)
++
++diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
++index eb23403..750a517 100644
++--- a/ssmlib/backends/btrfs.py
+++++ b/ssmlib/backends/btrfs.py
++@@ -119,7 +119,7 @@ class Btrfs(template.Backend):
++ fs_used = float(misc.get_real_size(array[6]))
++
++ elif array[0] == 'devid':
++- dev['dev_name'] = array[7]
+++ dev['dev_name'] = misc.get_real_device(array[7])
++
++ if not pool_name:
++ pool_name = self._find_uniq_pool_name(label, array[7])
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-ssm-force-btrfs-device-add.patch b/ssm-0.4-ssm-force-btrfs-device-add.patch
+new file mode 100644
+index 0000000..fd8d80f
+--- /dev/null
++++ b/ssm-0.4-ssm-force-btrfs-device-add.patch
+@@ -0,0 +1,341 @@
++From e5ca9b110954900f30bf32d66ea3440a4203e84c Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Wed, 15 Jan 2014 13:59:44 +0100
++Subject: [PATCH 03/10] ssm: force btrfs device add
++
++Btrfs is broken. It does not recognize the btrfs signature properly and
++even when the device has been wiped with wipefs btrfs would still claim
++that the device actually is 'btrfs' device which it is not!
++
++We've already fixed the case for mkfs.btrfs, howeve btrfs recently added
++the check for 'btrfs device add' as well, so we need to do the same
++thing. So make _can_btrfs_force() more generic and use it in extend()
++method as well.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/backends/btrfs.py | 19 +++++---
++ tests/unittests/test_btrfs.py | 102 +++++++++++++++++++++---------------------
++ 2 files changed, 65 insertions(+), 56 deletions(-)
++
++diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
++index 750a517..b238d52 100644
++--- a/ssmlib/backends/btrfs.py
+++++ b/ssmlib/backends/btrfs.py
++@@ -411,14 +411,13 @@ class BtrfsPool(Btrfs, template.BackendPool):
++ else:
++ self.data = self._pool
++
++- def _can_btrfs_force(self):
+++ def _can_btrfs_force(self, command):
++ """
++ This is just ridiculous. Unfortunately btrfs tools usually change
++ behaviour and options without bumping version number. So we have
++ to check whether btrfs allows to 'force' file system creation.
++ """
++- command=['mkfs.btrfs', '-f']
++- output = misc.run(command, can_fail=True)[1]
+++ output = misc.run(command + ['--force'], can_fail=True)[1]
++ found = re.search('invalid option', output)
++ if found:
++ return False
++@@ -456,8 +455,8 @@ class BtrfsPool(Btrfs, template.BackendPool):
++ # have tried to remove the device from the respective pool already.
++ # So at this point there should not be any useful signatures to
++ # speak of. However as I mentioned btrfs is broken, so force it.
++- if self._can_btrfs_force():
++- command.extend(['-f'])
+++ if self._can_btrfs_force(command):
+++ command.extend(['--force'])
++ command.extend(devs)
++ misc.run(command, stdout=True)
++ misc.send_udev_event(devs[0], "change")
++@@ -499,6 +498,16 @@ class BtrfsPool(Btrfs, template.BackendPool):
++ if type(devices) is not list:
++ devices = [devices]
++ command = ['device', 'add']
+++ # This might seem weird, but btrfs is mostly broken when it comes to
+++ # checking existing signatures because it will for example check for
+++ # backup superblocks as well, which is wrong. Also we have check for
+++ # existing file system signatures in the ssm itself. Other things
+++ # than file system should be covered by the backend and we should
+++ # have tried to remove the device from the respective pool already.
+++ # So at this point there should not be any useful signatures to
+++ # speak of. However as I mentioned btrfs is broken, so force it.
+++ if self._can_btrfs_force(['btrfs', 'device', 'add']):
+++ command.extend(['--force'])
++ command.extend(devices)
++ command.append(pool['mount'])
++ self.run_btrfs(command)
++diff --git a/tests/unittests/test_btrfs.py b/tests/unittests/test_btrfs.py
++index d1f8135..2fb04bc 100644
++--- a/tests/unittests/test_btrfs.py
+++++ b/tests/unittests/test_btrfs.py
++@@ -91,48 +91,48 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++
++ # Create volume using single device from non existent default pool
++ self._checkCmd("ssm create", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++
++ # Specify default backend
++ self._checkCmd("ssm -b btrfs create", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++
++ main.SSM_DEFAULT_BACKEND = 'lvm'
++ self._checkCmd("ssm --backend btrfs create", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++ main.SSM_DEFAULT_BACKEND = 'btrfs'
++
++- self._checkCmd("ssm -f create", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ self._checkCmd("ssm --force create", ['/dev/sda'],
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++
++ self._checkCmd("ssm -v create", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++
++- self._checkCmd("ssm -f -v create", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ self._checkCmd("ssm --force -v create", ['/dev/sda'],
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++
++ self._checkCmd("ssm create", ['-s 2.6T', '/dev/sda'],
++- "mkfs.btrfs -L {0} -b 2858730232217 -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L {0} -b 2858730232217 --force /dev/sda".format(default_pool))
++
++ self._checkCmd("ssm create", ['-r 0', '-s 2.6T', '/dev/sda'],
++- "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 --force /dev/sda".format(default_pool))
++ self._checkCmd("ssm create", ['-r 0', '-s 2.6T', '/dev/sda'],
++- "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 --force /dev/sda".format(default_pool))
++ self._checkCmd("ssm create", ['-r 1', '-s 512k', '/dev/sda /dev/sdb'],
++- "mkfs.btrfs -L btrfs_pool -m raid1 -d raid1 -b 524288 -f /dev/sda /dev/sdb".format(default_pool))
+++ "mkfs.btrfs -L btrfs_pool -m raid1 -d raid1 -b 524288 --force /dev/sda /dev/sdb".format(default_pool))
++ self._checkCmd("ssm create", ['-r 10', '-s 10M', '/dev/sda'],
++- "mkfs.btrfs -L btrfs_pool -m raid10 -d raid10 -b 10485760 -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L btrfs_pool -m raid10 -d raid10 -b 10485760 --force /dev/sda".format(default_pool))
++
++ # Create volume using single device from non existent my_pool
++ self._checkCmd("ssm create", ['--pool my_pool', '/dev/sda'],
++- "mkfs.btrfs -L my_pool -f /dev/sda")
+++ "mkfs.btrfs -L my_pool --force /dev/sda")
++
++ self._checkCmd("ssm create", ['-p my_pool', '-r 0', '-s 2.6T', '/dev/sda'],
++- "mkfs.btrfs -L my_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda")
+++ "mkfs.btrfs -L my_pool -m raid0 -d raid0 -b 2858730232217 --force /dev/sda")
++
++ # Create volume using multiple devices
++ self._checkCmd("ssm create /dev/sda /dev/sdb", [],
++- "mkfs.btrfs -L {0} -f /dev/sda /dev/sdb".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda /dev/sdb".format(default_pool))
++
++ # Create volume using single device from existing pool
++ self._addPool(default_pool, ['/dev/sdb', '/dev/sdd'])
++@@ -151,15 +151,15 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ # in the pool
++ self._checkCmd("ssm create", ['-n myvolume', '/dev/sda /dev/sdb'],
++ "btrfs subvolume create /tmp/mount/myvolume")
++- self._cmdEq("btrfs device add /dev/sda /tmp/mount", -2)
+++ self._cmdEq("btrfs device add --force /dev/sda /tmp/mount", -2)
++
++ self._checkCmd("ssm create", ['-p my_pool', '-n myvolume', '/dev/sdc2 /dev/sda'],
++ "btrfs subvolume create /tmp/mount/myvolume")
++- self._cmdEq("btrfs device add /dev/sda /mnt/test", -2)
+++ self._cmdEq("btrfs device add --force /dev/sda /mnt/test", -2)
++
++ self._checkCmd("ssm create", ['-n myvolume', '/dev/sda /dev/sdb /dev/sde'],
++ "btrfs subvolume create /tmp/mount/myvolume")
++- self._cmdEq("btrfs device add /dev/sda /dev/sde /tmp/mount", -2)
+++ self._cmdEq("btrfs device add --force /dev/sda /dev/sde /tmp/mount", -2)
++
++ def test_btrfs_remove(self):
++ # Generate some storage data
++@@ -267,17 +267,17 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ "btrfs filesystem resize 11723608063K /tmp/mount")
++ main.SSM_DEFAULT_BACKEND = 'btrfs'
++
++- self._cmdNotEq("btrfs device add /dev/sde /tmp/mount", -2)
+++ self._cmdNotEq("btrfs device add --force /dev/sde /tmp/mount", -2)
++
++ # Resize with enough space in the pool
++ self._checkCmd("ssm resize --size +1g", ['my_pool /dev/sde'],
++ "btrfs filesystem resize 1073052017K /mnt/test1")
++- self._cmdNotEq("btrfs device add /dev/sde /mnt/test1", -2)
+++ self._cmdNotEq("btrfs device add --force /dev/sde /mnt/test1", -2)
++
++ # Resize without enough space in the pool
++ self._checkCmd("ssm resize --size +1t", ['my_pool /dev/sde'],
++ "btrfs filesystem resize 2145745265K /mnt/test1")
++- self._cmdEq("btrfs device add /dev/sde /mnt/test1", -2)
+++ self._cmdEq("btrfs device add --force /dev/sde /mnt/test1", -2)
++
++ # Shrink volume
++ self._checkCmd("ssm resize", ['-s-100G', 'default_pool'],
++@@ -285,7 +285,7 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ self._checkCmd("ssm resize -s-500G", ['my_pool /dev/sde'],
++ "btrfs filesystem resize 547715441K /mnt/test1")
++ self.assertNotEqual(self.run_data[-2],
++- "btrfs device add /dev/sde /mnt/test1")
+++ "btrfs device add --force /dev/sde /mnt/test1")
++
++ # Set volume size
++ self._checkCmd("ssm resize", ['-s 10M', 'default_pool'],
++@@ -297,32 +297,32 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ self._checkCmd("ssm resize -s 20T default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 21474836480K /tmp/mount")
++ self.assertNotEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
++
++ self._checkCmd("ssm resize -s 22T default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 23622320128K /tmp/mount")
++ self.assertEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
++
++ self._checkCmd("ssm resize -s 3T my_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 3221225472K /mnt/test1")
++ self.assertEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /mnt/test1")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /mnt/test1")
++
++ self._checkCmd("ssm resize -s 3T my_pool /dev/sde /dev/sdc2",
++ [], "btrfs filesystem resize 3221225472K /mnt/test1")
++ self.assertEqual(self.run_data[-2],
++- "btrfs device add /dev/sde /mnt/test1")
+++ "btrfs device add --force /dev/sde /mnt/test1")
++
++ # Set volume in without the need adding more devices
++ self._checkCmd("ssm resize -s 10G default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 10485760K /tmp/mount")
++ self.assertNotEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
++ self._checkCmd("ssm resize -s 10G my_pool /dev/sdd /dev/sde",
++ [], "btrfs filesystem resize 10485760K /mnt/test1")
++ self.assertNotEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /mnt/test1")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /mnt/test1")
++
++ self._checkCmd("ssm list",
++ [], "btrfs filesystem resize 12247891967K /tmp/mount")
++@@ -330,23 +330,23 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ self._checkCmd("ssm resize -s +500G default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 12247891967K /tmp/mount")
++ self.assertEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
++
++ # Extend volume in without the need adding more devices
++ self._checkCmd("ssm resize -s 1k default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 1K /tmp/mount")
++ self.assertNotEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
++ self.assertNotEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
++
++ # Shrink volume with devices provided
++ self._checkCmd("ssm resize -s-10G default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 11713118207K /tmp/mount")
++ self.assertNotEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
++ self.assertNotEqual(self.run_data[-2],
++- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
++
++ def test_btrfs_add(self):
++ default_pool = btrfs.SSM_BTRFS_DEFAULT_POOL
++@@ -354,39 +354,39 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ # Adding to non existent pool
++ # Add device into default pool
++ self._checkCmd("ssm add", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++
++ # Specify backend
++ self._checkCmd("ssm --backend btrfs add", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++
++ main.SSM_DEFAULT_BACKEND = 'lvm'
++ self._checkCmd("ssm -b btrfs add", ['/dev/sda'],
++- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
++ main.SSM_DEFAULT_BACKEND = 'btrfs'
++
++ # Add more devices into default pool
++ self._checkCmd("ssm add", ['/dev/sda /dev/sdc1'],
++- "mkfs.btrfs -L {0} -f /dev/sda /dev/sdc1".format(default_pool))
+++ "mkfs.btrfs -L {0} --force /dev/sda /dev/sdc1".format(default_pool))
++ # Add device into defined pool
++ self._checkCmd("ssm add", ['-p my_pool', '/dev/sda'],
++- "mkfs.btrfs -L my_pool -f /dev/sda")
+++ "mkfs.btrfs -L my_pool --force /dev/sda")
++ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sda'],
++- "mkfs.btrfs -L my_pool -f /dev/sda")
+++ "mkfs.btrfs -L my_pool --force /dev/sda")
++ # Add more devices into defined pool
++ self._checkCmd("ssm add", ['-p my_pool', '/dev/sda /dev/sdc1'],
++- "mkfs.btrfs -L my_pool -f /dev/sda /dev/sdc1")
+++ "mkfs.btrfs -L my_pool --force /dev/sda /dev/sdc1")
++ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sda /dev/sdc1'],
++- "mkfs.btrfs -L my_pool -f /dev/sda /dev/sdc1")
+++ "mkfs.btrfs -L my_pool --force /dev/sda /dev/sdc1")
++
++ # Adding to existing default pool
++ self._addPool(default_pool, ['/dev/sdb', '/dev/sdd'])
++ # Add device into default pool
++ self._checkCmd("ssm add", ['/dev/sda'],
++- "btrfs device add /dev/sda /tmp/mount")
+++ "btrfs device add --force /dev/sda /tmp/mount")
++ # Add more devices into default pool
++ self._checkCmd("ssm add", ['/dev/sda /dev/sdc1'],
++- "btrfs device add /dev/sda /dev/sdc1 /tmp/mount")
+++ "btrfs device add --force /dev/sda /dev/sdc1 /tmp/mount")
++
++ # Adding to existing defined pool
++ self._addPool('my_pool', ['/dev/sdc2', '/dev/sdc3'])
++@@ -394,25 +394,25 @@ class BtrfsFunctionCheck(MockSystemDataSource):
++ '/mnt/test1')
++ # Add device into defined pool
++ self._checkCmd("ssm add", ['-p my_pool', '/dev/sda'],
++- "btrfs device add /dev/sda /mnt/test1")
+++ "btrfs device add --force /dev/sda /mnt/test1")
++ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sda'],
++- "btrfs device add /dev/sda /mnt/test1")
+++ "btrfs device add --force /dev/sda /mnt/test1")
++ # Add more devices into defined pool
++ self._checkCmd("ssm add", ['-p my_pool', '/dev/sda /dev/sdc1'],
++- "btrfs device add /dev/sda /dev/sdc1 /mnt/test1")
+++ "btrfs device add --force /dev/sda /dev/sdc1 /mnt/test1")
++ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sda /dev/sdc1'],
++- "btrfs device add /dev/sda /dev/sdc1 /mnt/test1")
+++ "btrfs device add --force /dev/sda /dev/sdc1 /mnt/test1")
++ # Add verbose
++ self._checkCmd("ssm -v add", ['--pool {0}'.format(default_pool),
++ '/dev/sda /dev/sdc1'],
++- "btrfs device add /dev/sda /dev/sdc1 /tmp/mount")
+++ "btrfs device add --force /dev/sda /dev/sdc1 /tmp/mount")
++
++ # Add two devices into existing pool (one of the devices already is in
++ # the pool
++ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sdc2 /dev/sda'],
++- "btrfs device add /dev/sda /mnt/test1")
+++ "btrfs device add --force /dev/sda /mnt/test1")
++ self._checkCmd("ssm add", ['/dev/sda /dev/sdb'],
++- "btrfs device add /dev/sda /tmp/mount")
+++ "btrfs device add --force /dev/sda /tmp/mount")
++
++ def test_btrfs_mount(self):
++ self._addDir("/mnt/test")
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch b/ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch
+new file mode 100644
+index 0000000..890a04f
+--- /dev/null
++++ b/ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch
+@@ -0,0 +1,27 @@
++From 0e461e0fb15c47513faf0f27b553624ea51b069b Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Wed, 15 Jan 2014 17:39:00 +0100
++Subject: [PATCH 06/10] test 008: Device count should go down after removing a
++ device
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ tests/bashtests/008-btrfs-remove.sh | 2 +-
++ 1 file changed, 1 insertion(+), 1 deletion(-)
++
++diff --git a/tests/bashtests/008-btrfs-remove.sh b/tests/bashtests/008-btrfs-remove.sh
++index fd9b91e..4e40e99 100755
++--- a/tests/bashtests/008-btrfs-remove.sh
+++++ b/tests/bashtests/008-btrfs-remove.sh
++@@ -93,7 +93,7 @@ export SSM_DEFAULT_BACKEND='btrfs'
++ not check btrfs_fs_field $pool1 label $pool1
++ not check btrfs_vol_field $mnt1 subvolume $vol1
++ not check btrfs_vol_field $mnt2 subvolume $vol2
++-check btrfs_fs_field $SSM_BTRFS_DEFAULT_POOL dev_count 5
+++check btrfs_fs_field $SSM_BTRFS_DEFAULT_POOL dev_count 4
++ umount_all
++ ssm -f remove --all
++
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch b/ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch
+new file mode 100644
+index 0000000..64c658e
+--- /dev/null
++++ b/ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch
+@@ -0,0 +1,27 @@
++From a5da549a380d83db0ee4b9bed70ec3575e6c2f37 Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Wed, 15 Jan 2014 15:52:34 +0100
++Subject: [PATCH 04/10] tests: Fix they way we gather information about btrfs
++ file system
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ tests/bashtests/lib/check.sh | 2 +-
++ 1 file changed, 1 insertion(+), 1 deletion(-)
++
++diff --git a/tests/bashtests/lib/check.sh b/tests/bashtests/lib/check.sh
++index 9a8f853..e9a7f39 100644
++--- a/tests/bashtests/lib/check.sh
+++++ b/tests/bashtests/lib/check.sh
++@@ -323,7 +323,7 @@ crypt_vol_field()
++
++ btrfs_fs_field()
++ {
++- lines=$(btrfs filesystem show 2> /dev/null | grep -A 1 "'$1'" || true)
+++ lines=$(btrfs filesystem show 2> /dev/null | grep -A 1 "^Label:.*$1" || true)
++
++ case $2 in
++ "label")
++--
++1.8.3.1
++
+diff --git a/ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch b/ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch
+new file mode 100644
+index 0000000..f690ff3
+--- /dev/null
++++ b/ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch
+@@ -0,0 +1,38 @@
++From c3609263094816eee96bef416f7ed72b49c8f2b8 Mon Sep 17 00:00:00 2001
++From: Lukas Czerner <lczerner at redhat.com>
++Date: Wed, 15 Jan 2014 16:51:18 +0100
++Subject: [PATCH 05/10] tests: Make btrfs recognize devices from tests suite
++ properly
++
++This is ugly hack to fix a problem with test suite and btrfs
++where ?sometimes? btrfs prints out device name in the path
++of the test suite rather than path in the real '/dev/'
++directory. This should cover that without any impact on
++real usage.
++
++Signed-off-by: Lukas Czerner <lczerner at redhat.com>
++---
++ ssmlib/backends/btrfs.py | 7 +++++++
++ 1 file changed, 7 insertions(+)
++
++diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
++index b238d52..836f108 100644
++--- a/ssmlib/backends/btrfs.py
+++++ b/ssmlib/backends/btrfs.py
++@@ -119,6 +119,13 @@ class Btrfs(template.Backend):
++ fs_used = float(misc.get_real_size(array[6]))
++
++ elif array[0] == 'devid':
+++ # This is ugly hack to fix a problem with test suite and btrfs
+++ # where ?sometimes? btrfs prints out device name in the path
+++ # of the test suite rather than path in the real '/dev/'
+++ # directory. This should cover that without any impact on
+++ # real usage
+++ if not os.path.islink(array[7]):
+++ array[7] = re.sub(r'.*/dev/', '/dev/', array[7])
++ dev['dev_name'] = misc.get_real_device(array[7])
++
++ if not pool_name:
++--
++1.8.3.1
++
+diff --git a/system-storage-manager.spec b/system-storage-manager.spec
+index 7f1947d..ed384ba 100644
+--- a/system-storage-manager.spec
++++ b/system-storage-manager.spec
+@@ -1,7 +1,7 @@
+ %{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}}
+
+ Name: system-storage-manager
+-Version: 0.2
++Version: 0.4
+ Release: 4%{?dist}
+ Summary: A single tool to manage your storage
+
+@@ -10,6 +10,22 @@ License: GPLv2+
+ URL: http://storagemanager.sf.net
+ Source0: http://downloads.sourceforge.net/storagemanager/%{name}-%{version}.tar.gz
+
++
++Patch1: ssm-0.4-ssm-big-update-no-1.patch
++Patch2: ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch
++Patch3: ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch
++Patch4: ssm-0.4-ssm-force-btrfs-device-add.patch
++Patch5: ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch
++Patch6: ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch
++Patch7: ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch
++Patch8: ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch
++Patch9: ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch
++Patch10: ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch
++Patch11: ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch
++Patch12: ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch
++Patch13: ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch
++
++
+ BuildArch: noarch
+ BuildRequires: python-devel >= 2.6
+ Requires: python >= 2.6
+@@ -43,6 +59,33 @@ technologies via a single unified interface.
+ %prep
+ %setup -q
+
++# Contains upstream patches
++# 39eb3058a02a2b482c4c5bdf0120b70605a6b23a..4210d7560585f2a241eaa5ebc364c98d49f565d2
++%patch1 -p1
++# misc: get_unit_size() accept different unit formats
++%patch2 -p1
++# ssm: btrfs backend: try to find the real device
++%patch3 -p1
++# ssm: force btrfs device add
++%patch4 -p1
++# tests: Fix they way we gather information about btrfs
++%patch5 -p1
++# tests: Make btrfs recognize devices from tests suite properly
++%patch6 -p1
++# test 008: Device count should go down after removing a device
++%patch7 -p1
++# misc: Fix get_device_size to work with partitions correctly
++%patch8 -p1
++# ssm: Suppress backtrace if command failed
++%patch9 -p1
++# ssm: Add SSM_PRINT_BACKTRACE environment variable
++%patch10 -p1
++# misc: Use wipefs -a in misc.wipefs() helper
++%patch11 -p1
++# ssm: Fix various problems found by pylint
++%patch12 -p1
++# ssm: Remove unnecessary /usr/bin/env python
++%patch13 -p1
+
+ %build
+ # nothing to build
+@@ -55,6 +98,9 @@ if [ "%{_pkgdocdir}" != "%{_docdir}/%{name}-%{version}" ]; then
+ mv ${RPM_BUILD_ROOT}/{%{_docdir}/%{name}-%{version},%{_pkgdocdir}}
+ fi
+
++%check
++make test
++
+
+ %files
+ %{_bindir}/ssm
+@@ -65,6 +111,26 @@ fi
+
+
+ %changelog
++* Mon Jan 20 2014 Lukas Czerner <lczerner at redhat.com> 0.4-4
++- Update to a new upstream release v0.4
++- Remove btrfs resize support
++- Unmount all btrfs subvolumes when removing a filesystem
++- Fix size argument parsing for create and snapshot command
++- Fix list output for some cases
++- Add support to create encrypted volumes with crypt backend
++- Add dry-run option
++- Fix removing volumes with crypt backend
++- Add raid1 and raid10 support for lvm backend
++- Allow to check btrfs volumes
++- Fix error handling when trying to resize btrfs subvolume
++- Fix ssm mount command so it detects directory properly
++- Suppress backtrace when a command fails
++- Fix ssm to recognize units in new btrfs output properly
++- Use correct sysfs file to get size for a partition
++- Fix ssm to be able add a device with signature to btrfs file system
++- Resognize btrfs devices from new btrfs output properly
++
++
+ * Mon Dec 16 2013 Ville Skyttä <ville.skytta at iki.fi> - 0.2-4
+ - Install docs to %%{_pkgdocdir} where available (#994122).
+
+--
+1.8.3.1
+
diff --git a/sources b/sources
index 038df21..f176aa7 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-524654f3b7bab544c4e612470b5d2392 system-storage-manager-0.2.tar.gz
+43235208daf95f8ada1b0632bf7bc756 system-storage-manager-0.4.tar.gz
diff --git a/ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch b/ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch
new file mode 100644
index 0000000..3aac93c
--- /dev/null
+++ b/ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch
@@ -0,0 +1,39 @@
+From 5b917ffbddfbb7366d2ad7d5f0e9c016ced29ca5 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Wed, 15 Jan 2014 17:52:28 +0100
+Subject: [PATCH 07/10] misc: Fix get_device_size to work with partitions
+ correctly
+
+Currently get_device_size() searches for the size in sysfs specifically
+(/sys/block/<devname>/size) however that is not the right palce for
+partitions since partitions are subdirectories in /sys/block/<devname>
+directory.
+
+So change get_device_size() to get major and minor numbers of the device
+in question and use sysfs file /sys/dev/block/<major>:<minor>/size to
+get the size.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/misc.py | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/ssmlib/misc.py b/ssmlib/misc.py
+index bfaa1c4..dbfa6b1 100644
+--- a/ssmlib/misc.py
++++ b/ssmlib/misc.py
+@@ -608,8 +608,9 @@ def is_bdevice(path):
+
+
+ def get_device_size(device):
+- devname = device.split('/')[-1]
+- with open("/sys/block/{0}/size".format(devname), 'r') as f:
++ info = os.stat(device)
++ major, minor = divmod(info.st_rdev, 256)
++ with open("/sys/dev/block/{0}:{1}/size".format(major, minor), 'r') as f:
+ for line in f:
+ size = int(line)/2
+ return size
+--
+1.8.3.1
+
diff --git a/ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch b/ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch
new file mode 100644
index 0000000..9a11955
--- /dev/null
+++ b/ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch
@@ -0,0 +1,103 @@
+From 37b31373b1408c299270e3134b88eff4528ca75a Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Thu, 16 Jan 2014 17:31:57 +0100
+Subject: [PATCH 10/10] misc: Use wipefs -a in misc.wipefs() helper
+
+Currently we only wipe the first signature we can find with wipefs.
+However wipefs has an ability to wipe all the signatures of given type
+from the device (yes this is once again about btrfs) with option '-a'.
+So use that instead. This also simplify the wipefs helper a lot.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/misc.py | 14 ++------------
+ tests/unittests/test_btrfs.py | 34 +++++++++++++++++-----------------
+ 2 files changed, 19 insertions(+), 29 deletions(-)
+
+diff --git a/ssmlib/misc.py b/ssmlib/misc.py
+index 3d99c07..915cad1 100644
+--- a/ssmlib/misc.py
++++ b/ssmlib/misc.py
+@@ -363,18 +363,8 @@ def get_dmnumber(name):
+ def wipefs(device, signatures):
+ if type(signatures) is not list:
+ signatures = [signatures]
+- command = ['wipefs', '-p', device]
+- output = run(command)[1]
+- offset = []
+- for line in output[1:].split('\n'):
+- if not line:
+- continue
+- array = line.split(",")
+- if array[-1] in signatures:
+- offset.extend(['--offset', array[0]])
+- if len(offset) > 1:
+- command = ['wipefs'] + offset + [device]
+- run(command)
++ command = ['wipefs', '-a', '-t', ','.join(signatures), device]
++ run(command)
+
+
+ def humanize_size(arg):
+diff --git a/tests/unittests/test_btrfs.py b/tests/unittests/test_btrfs.py
+index 2fb04bc..2b6cd8e 100644
+--- a/tests/unittests/test_btrfs.py
++++ b/tests/unittests/test_btrfs.py
+@@ -167,12 +167,12 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ self._addPool('my_pool', ['/dev/sdc2', '/dev/sdc3', '/dev/sdc1'])
+
+ # remove volume
+- self._checkCmd("ssm remove default_pool", [], "wipefs -p /dev/sda")
+- self._cmdEq("wipefs -p /dev/sdb", -2)
++ self._checkCmd("ssm remove default_pool", [], "wipefs -a -t btrfs /dev/sda")
++ self._cmdEq("wipefs -a -t btrfs /dev/sdb", -2)
+
+- self._checkCmd("ssm remove my_pool", [], "wipefs -p /dev/sdc3")
+- self._cmdEq("wipefs -p /dev/sdc1", -2)
+- self._cmdEq("wipefs -p /dev/sdc2", -3)
++ self._checkCmd("ssm remove my_pool", [], "wipefs -a -t btrfs /dev/sdc3")
++ self._cmdEq("wipefs -a -t btrfs /dev/sdc1", -2)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdc2", -3)
+
+ # remove subvolume
+ self._addVol('vol001', 117283225, 1, 'default_pool', ['/dev/sda'], '/mnt/test')
+@@ -189,24 +189,24 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ self._addPool('other_pool', ['/dev/sdd', '/dev/sde'])
+ self._checkCmd("ssm remove /dev/sdd /dev/sdb other_pool my_pool default_pool:/dev/default_pool/vol001", [],
+ "btrfs subvolume delete /mnt/test")
+- self._cmdEq("wipefs -p /dev/sdc1", -2)
+- self._cmdEq("wipefs -p /dev/sdc3", -3)
+- self._cmdEq("wipefs -p /dev/sdc2", -4)
+- self._cmdEq("wipefs -p /dev/sde", -5)
+- self._cmdEq("wipefs -p /dev/sdd", -6)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdc1", -2)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdc3", -3)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdc2", -4)
++ self._cmdEq("wipefs -a -t btrfs /dev/sde", -5)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdd", -6)
+ self._cmdEq("btrfs device delete /dev/sdb /mnt/test", -7)
+ self._cmdEq("btrfs device delete /dev/sdd /tmp/mount", -8)
+
+ self._removeMount("/dev/sda")
+ # remove all
+ self._checkCmd("ssm remove --all", [],
+- "wipefs -p /dev/sde")
+- self._cmdEq("wipefs -p /dev/sdd", -2)
+- self._cmdEq("wipefs -p /dev/sdc1", -3)
+- self._cmdEq("wipefs -p /dev/sdc3", -4)
+- self._cmdEq("wipefs -p /dev/sdc2", -5)
+- self._cmdEq("wipefs -p /dev/sda", -6)
+- self._cmdEq("wipefs -p /dev/sdb", -7)
++ "wipefs -a -t btrfs /dev/sde")
++ self._cmdEq("wipefs -a -t btrfs /dev/sdd", -2)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdc1", -3)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdc3", -4)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdc2", -5)
++ self._cmdEq("wipefs -a -t btrfs /dev/sda", -6)
++ self._cmdEq("wipefs -a -t btrfs /dev/sdb", -7)
+
+ # TODO
+ # remove force
+--
+1.8.3.1
+
diff --git a/ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch b/ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch
new file mode 100644
index 0000000..e74536a
--- /dev/null
+++ b/ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch
@@ -0,0 +1,221 @@
+From ef26cf6fb0e33ca52f35fcf219fd21db7788c200 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Mon, 13 Jan 2014 16:29:01 +0100
+Subject: [PATCH 01/10] misc: get_unit_size() accept different unit formats
+
+Because btrfs recently changed it's output to use MiB format instead of
+MB format the function handling this within the btrfs is broken.
+
+Fix this my making get_unit_size() to handle different unit formats (M,
+MB, MiB) and use that in btrfs as well.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/backends/btrfs.py | 18 ++---------
+ ssmlib/misc.py | 82 ++++++++++++++++++++++++++++++++++--------------
+ test.py | 13 ++++----
+ 3 files changed, 69 insertions(+), 44 deletions(-)
+
+diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
+index a4aedda..eb23403 100644
+--- a/ssmlib/backends/btrfs.py
++++ b/ssmlib/backends/btrfs.py
+@@ -33,18 +33,6 @@ except KeyError:
+ SSM_BTRFS_DEFAULT_POOL = "btrfs_pool"
+
+
+-def get_real_number(string):
+- number = float(string[0:-2])
+- unit = string[-2:-1]
+- # The result will be in kilobytes
+- units = ["K", "M", "G", "T", "P", "E", "Z", "Y"]
+- for i, u in enumerate(units):
+- if u == unit:
+- number *= (2 ** (i * 10))
+- break
+- return number
+-
+-
+ def get_btrfs_version():
+ try:
+ output = misc.run(['btrfs', '--version'], can_fail=True)[1]
+@@ -128,7 +116,7 @@ class Btrfs(template.Backend):
+
+ elif array[0] == 'Total':
+ pool['dev_count'] = array[2]
+- fs_used = get_real_number(array[6])
++ fs_used = float(misc.get_real_size(array[6]))
+
+ elif array[0] == 'devid':
+ dev['dev_name'] = array[7]
+@@ -154,9 +142,9 @@ class Btrfs(template.Backend):
+ vol['real_dev'] = found[0].split(':')[0]
+ break
+
+- dev_used = get_real_number(array[5])
++ dev_used = float(misc.get_real_size(array[5]))
+ dev['dev_used'] = str(dev_used)
+- fs_size += get_real_number(array[3])
++ fs_size += float(misc.get_real_size(array[3]))
+
+ dev_size = \
+ int(partitions[dev['dev_name'].rpartition("/")[-1]][2])
+diff --git a/ssmlib/misc.py b/ssmlib/misc.py
+index 3ff1577..bfaa1c4 100644
+--- a/ssmlib/misc.py
++++ b/ssmlib/misc.py
+@@ -32,34 +32,57 @@ TMP_MOUNTED = []
+
+ def get_unit_size(string):
+ """
+- Check the last character of the string for the unit and return the unit
+- value, otherwise return zero. It check only the last character of the
+- string.
++ Check the last character of the string for the unit and return the tuple
++ of unit value, unit name, otherwise return zero. It check only the first
++ character of the unit string.
+
+ >>> get_unit_size("B")
+- 1
++ (1, 'B')
+ >>> get_unit_size("k")
+- 1024
+- >>> get_unit_size("M")
+- 1048576
+- >>> get_unit_size("g")
+- 1073741824
+- >>> get_unit_size("T")
+- 1099511627776...
+- >>> get_unit_size("p")
+- 1125899906842624...
++ (1024, 'k')
++ >>> get_unit_size("KiB")
++ (1024, 'KiB')
++ >>> get_unit_size("KB")
++ (1024, 'KB')
++ >>> get_unit_size("10M")
++ (1048576, 'M')
++ >>> get_unit_size("-99.99g")
++ (1073741824, 'g')
++ >>> get_unit_size("-99.99GiB")
++ (1073741824, 'GiB')
++ >>> get_unit_size("+99.99G")
++ (1073741824, 'G')
++ >>> get_unit_size("+99.99gb")
++ (1073741824, 'gb')
++ >>> get_unit_size("99.99g")
++ (1073741824, 'g')
++ >>> get_unit_size("0.84T")
++ (1099511627776, 'T')
++ >>> get_unit_size("0.84TiB")
++ (1099511627776, 'TiB')
++ >>> get_unit_size("0p")
++ (1125899906842624, 'p')
++ >>> get_unit_size("99kit")
++ (0, '')
+ >>> get_unit_size("")
+- 0
++ (0, '')
+ >>> get_unit_size("H")
+- 0
++ (0, '')
+ """
+
+ mult = 0
+ units = {'B': 1, 'K': 2 ** 10, 'M': 2 ** 20, 'G': 2 ** 30, 'T': 2 ** 40,
+ 'P': 2 ** 50}
+- if len(string) > 0 and string[-1].upper() in units:
+- mult = units[string[-1].upper()]
+- return mult
++ unit = re.sub(r'^\+?-?\d+(\.\d*)?', '', string)
++ if len(unit) > 0 and unit[0].upper() in units:
++ mult = units[unit[0].upper()]
++ all_units = {'B', 'K', 'M', 'G', 'T', 'P',
++ 'KB', 'MB', 'GB', 'TB', 'PB',
++ 'KIB', 'MIB', 'GIB', 'TIB', 'PIB'}
++ if unit.upper() in all_units:
++ return mult, unit
++ else:
++ return 0, ""
+
+
+ def is_number(string):
+@@ -92,8 +115,14 @@ def get_real_size(size):
+
+ >>> get_real_size("3141")
+ '3141'
++ >>> get_real_size("3141B")
++ '3.07'
+ >>> get_real_size("3141K")
+ '3141.00'
++ >>> get_real_size("3141KB")
++ '3141.00'
++ >>> get_real_size("3141KiB")
++ '3141.00'
+ >>> get_real_size("3141k")
+ '3141.00'
+ >>> get_real_size("3141M")
+@@ -118,6 +147,10 @@ def get_real_size(size):
+ '-3.14'
+ >>> get_real_size("3.14G")
+ '3292528.64'
++ >>> get_real_size("3.14GB")
++ '3292528.64'
++ >>> get_real_size("3.14GiB")
++ '3292528.64'
+ >>> get_real_size("+3.14g")
+ '+3292528.64'
+ >>> get_real_size("-3.14G")
+@@ -133,12 +166,15 @@ def get_real_size(size):
+ """
+ if is_number(size):
+ return size
+- elif is_number(size[:-1]):
++ else:
+ # Always use kilobytes in ssm
+- mult = get_unit_size(size) / 1024
+- sign = '+' if size[0] == '+' else ''
+- if mult:
+- return '{0}{1:.2f}'.format(sign, float(size[:-1]) * mult)
++ mult, unit = get_unit_size(size)
++ mult /= float(1024)
++ number = re.sub(unit + "$", '', size)
++ if is_number(number):
++ sign = '+' if size[0] == '+' else ''
++ if mult:
++ return '{0}{1:.2f}'.format(sign, float(number) * mult)
+ raise Exception("Not supported unit in the " +
+ "size \'{0}\' argument.".format(size))
+
+diff --git a/test.py b/test.py
+index 2ca542c..7c91ae8 100644
+--- a/test.py
++++ b/test.py
+@@ -89,17 +89,18 @@ def run_bash_tests():
+
+ def quick_test():
+ print "[+] Running doctests"
+- doctest_flags = doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS
++ doctest_flags = doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS | \
++ doctest.REPORT_ONLY_FIRST_FAILURE
+ result = doctest.testmod(main, exclude_empty=True, report=True,
+- raise_on_error=True, optionflags=doctest_flags)
++ raise_on_error=False, optionflags=doctest_flags)
+ result = doctest.testmod(lvm, exclude_empty=True, report=True,
+- raise_on_error=True, optionflags=doctest_flags)
++ raise_on_error=False, optionflags=doctest_flags)
+ result = doctest.testmod(crypt, exclude_empty=True, report=True,
+- raise_on_error=True, optionflags=doctest_flags)
++ raise_on_error=False, optionflags=doctest_flags)
+ result = doctest.testmod(btrfs, exclude_empty=True, report=True,
+- raise_on_error=True, optionflags=doctest_flags)
++ raise_on_error=False, optionflags=doctest_flags)
+ result = doctest.testmod(misc, exclude_empty=True, report=True,
+- raise_on_error=True, optionflags=doctest_flags)
++ raise_on_error=False, optionflags=doctest_flags)
+ print "[+] Running unittests"
+ test_loader = unittest.TestLoader()
+ tests_lvm = test_loader.loadTestsFromModule(test_lvm)
+--
+1.8.3.1
+
diff --git a/ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch b/ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch
new file mode 100644
index 0000000..e305100
--- /dev/null
+++ b/ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch
@@ -0,0 +1,57 @@
+From e5577724d859ca19e6b3074f05f25bc43c748a43 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Thu, 16 Jan 2014 15:08:11 +0100
+Subject: [PATCH 09/10] ssm: Add SSM_PRINT_BACKTRACE environment variable
+
+Currently we're catching all the SsmError exception and just print out
+simple error message. This is fine, however it makes debugging harder in
+some cases, so add SSM_PRINT_BACKTRACE environment variable which
+enables us to print traceback when needed. Simply set the variable to
+yes, true, or 1 and ssm will print traceback in case of error.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ bin/ssm | 20 +++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/bin/ssm b/bin/ssm
+index baa3c60..6058f64 100755
+--- a/bin/ssm
++++ b/bin/ssm
+@@ -19,8 +19,23 @@
+
+ import os
+ import sys
++import traceback
+ from ssmlib import problem
+
++
++# Should we print backtrace on error, or suppress it ?
++# Suppress it by default.
++try:
++ SSM_PRINT_BACKTRACE = os.environ['SSM_PRINT_BACKTRACE']
++ if SSM_PRINT_BACKTRACE.upper() in ['YES', 'TRUE', '1']:
++ SSM_PRINT_BACKTRACE = True
++ else:
++ SSM_PRINT_BACKTRACE = False
++except KeyError:
++ SSM_PRINT_BACKTRACE = False
++
++
++
+ try:
+ from ssmlib import main
+
+@@ -32,5 +47,8 @@ try:
+ sys.exit("\nRoot privileges required to run this script!\n")
+ sys.exit(main.main())
+ except problem.SsmError, err:
+- print str(err)
++ if SSM_PRINT_BACKTRACE is True:
++ traceback.print_exc(file=sys.stdout)
++ else:
++ print str(err)
+ sys.exit(err.errcode)
+--
+1.8.3.1
+
diff --git a/ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch b/ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch
new file mode 100644
index 0000000..f436c38
--- /dev/null
+++ b/ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch
@@ -0,0 +1,222 @@
+From 0dcf4bcee5c5f6503905dac298dee77f1141f783 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Fri, 17 Jan 2014 16:05:56 +0100
+Subject: [PATCH] ssm: Fix various problems found by pylint
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/__init__.py | 2 +-
+ ssmlib/backends/btrfs.py | 12 +++++-------
+ ssmlib/backends/crypt.py | 5 ++---
+ ssmlib/backends/lvm.py | 3 +--
+ ssmlib/backends/md.py | 3 ---
+ ssmlib/main.py | 6 +++---
+ ssmlib/misc.py | 12 ++++++------
+ 7 files changed, 18 insertions(+), 25 deletions(-)
+
+diff --git a/ssmlib/__init__.py b/ssmlib/__init__.py
+index f1bcfdb..0a49de2 100644
+--- a/ssmlib/__init__.py
++++ b/ssmlib/__init__.py
+@@ -15,4 +15,4 @@
+ # You should have received a copy of the GNU General Public License
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+-__all__ = ["backends", "misc", "main", "problem"]
++__all__ = ["backend", "misc", "main", "problem"]
+diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
+index 836f108..acd5446 100644
+--- a/ssmlib/backends/btrfs.py
++++ b/ssmlib/backends/btrfs.py
+@@ -19,10 +19,8 @@
+
+ import re
+ import os
+-import sys
+ import datetime
+ from ssmlib import misc
+-from ssmlib import problem
+ from ssmlib.backends import template
+
+ __all__ = ["BtrfsVolume", "BtrfsPool", "BtrfsDev"]
+@@ -37,7 +35,7 @@ def get_btrfs_version():
+ try:
+ output = misc.run(['btrfs', '--version'], can_fail=True)[1]
+ output = output.strip().split("\n")[-1]
+- version = re.search('(?<=v)\d+\.\d+', output).group(0)
++ version = re.search(r'(?<=v)\d+\.\d+', output).group(0)
+ except (OSError, AttributeError):
+ version = "0.0"
+ return float(version)
+@@ -262,7 +260,7 @@ class Btrfs(template.Backend):
+ new['hide'] = False
+ # Store snapshot info
+ if 'mount' in new and \
+- re.match("snap-\d{4}-\d{2}-\d{2}-T\d{6}",
++ re.match(r"snap-\d{4}-\d{2}-\d{2}-T\d{6}",
+ os.path.basename(new['mount'])):
+ new['snap_name'] = "{0}:{1}".format(name,
+ os.path.basename(new['path']))
+@@ -279,8 +277,8 @@ class Btrfs(template.Backend):
+ continue
+ # For the version with screwed 'subvolume list' command
+ line = re.sub("<FS_TREE>/*", "", line)
+- volume['ID'] = re.search('(?<=ID )\d+', line).group(0)
+- volume['top_level'] = re.search('(?<=top level )\d+', line).group(0)
++ volume['ID'] = re.search(r'(?<=ID )\d+', line).group(0)
++ volume['top_level'] = re.search(r'(?<=top level )\d+', line).group(0)
+ volume['path'] = re.search('(?<=path ).*$', line).group(0)
+ volume['subvolume'] = True
+ yield volume
+@@ -450,7 +448,7 @@ class BtrfsPool(Btrfs, template.BackendPool):
+ command.extend(['-m', 'raid10', '-d', 'raid10'])
+ else:
+ raise Exception("Btrfs backed currently does not support " +
+- "RAID level {0}".format(raid['level']))
++ "RAID level {0}".format(options['raid']))
+
+ if size:
+ command.extend(['-b', "{0}".format(int(float(size) * 1024))])
+diff --git a/ssmlib/backends/crypt.py b/ssmlib/backends/crypt.py
+index 4aad266..8c1b48c 100644
+--- a/ssmlib/backends/crypt.py
++++ b/ssmlib/backends/crypt.py
+@@ -21,7 +21,6 @@ import re
+ import os
+ import stat
+ from ssmlib import misc
+-from ssmlib import problem
+ from ssmlib.backends import template
+
+ __all__ = ["DmCryptVolume"]
+@@ -240,7 +239,7 @@ class DmCryptDevice(DmObject, template.BackendDevice):
+ device = {}
+ devname = "/dev/" + line[3]
+ signature = misc.get_signature(devname)
+- if misc.get_signature(devname) in CRYPT_SIGNATURES:
++ if signature in CRYPT_SIGNATURES:
+ device['hide'] = False
+ device['dev_name'] = devname
+ device['pool_name'] = self.default_pool_name
+@@ -250,4 +249,4 @@ class DmCryptDevice(DmObject, template.BackendDevice):
+
+
+ def remove(self, devices):
+- misc.wipefs(device, CRYPT_SIGNATURES)
++ misc.wipefs(devices, CRYPT_SIGNATURES)
+diff --git a/ssmlib/backends/lvm.py b/ssmlib/backends/lvm.py
+index 955c705..4c7c0ad 100644
+--- a/ssmlib/backends/lvm.py
++++ b/ssmlib/backends/lvm.py
+@@ -22,7 +22,6 @@ import os
+ import stat
+ import datetime
+ from ssmlib import misc
+-from ssmlib import problem
+ from ssmlib.backends import template
+
+ __all__ = ["PvsInfo", "VgsInfo", "LvsInfo"]
+@@ -47,7 +46,7 @@ def get_lvm_version():
+ for line in output:
+ if pattern.match(line.strip()):
+ match = " ".join(line.split())
+- tmp = re.search('(?<=LVM version: )\d+\.\d+\.\d+',
++ tmp = re.search(r'(?<=LVM version: )\d+\.\d+\.\d+',
+ match).group(0)
+ version = map(int, tmp.split(".", 3))
+ except (OSError, AttributeError):
+diff --git a/ssmlib/backends/md.py b/ssmlib/backends/md.py
+index f33243c..1e3c6e2 100644
+--- a/ssmlib/backends/md.py
++++ b/ssmlib/backends/md.py
+@@ -18,11 +18,8 @@
+ # md module for System Storage Manager
+
+ import os
+-import stat
+ import socket
+-import datetime
+ from ssmlib import misc
+-from ssmlib import problem
+ from ssmlib.backends import template
+
+ try:
+diff --git a/ssmlib/main.py b/ssmlib/main.py
+index 7aab312..c294aec 100644
+--- a/ssmlib/main.py
++++ b/ssmlib/main.py
+@@ -1284,7 +1284,7 @@ class StorageHandle(object):
+ try:
+ pool.remove()
+ removed += 1
+- except (RuntimeError, problem.SsmError), ex:
++ except (RuntimeError, problem.SsmError):
+ PR.info("Unable to remove '{0}'".format(pool['pool_name']))
+ ret = False
+ if removed == 0:
+@@ -1378,7 +1378,6 @@ class StorageHandle(object):
+ dev = self.dev[real]
+ if dev and 'fs_info' in dev:
+ return dev
+- err = "'{0}' does not contain valid file system".format(real)
+ return False
+
+ def _find_device_record(self, path):
+@@ -1544,6 +1543,7 @@ def valid_resize_size(size):
+
+ def is_directory(string):
+ if string is None:
++ err = "Directory name not defined."
+ raise argparse.ArgumentTypeError(err)
+ try:
+ mode = os.stat(string).st_mode
+@@ -1847,7 +1847,7 @@ def main(args=None):
+ sys.exitfunc = misc.do_cleanup
+
+ if args.dry_run:
+- return 0;
++ return 0
+
+ try:
+ args.func(args)
+diff --git a/ssmlib/misc.py b/ssmlib/misc.py
+index 915cad1..77ddc2d 100644
+--- a/ssmlib/misc.py
++++ b/ssmlib/misc.py
+@@ -77,9 +77,9 @@ def get_unit_size(string):
+ unit = re.sub(r'^\+?-?\d+(\.\d*)?', '', string)
+ if len(unit) > 0 and unit[0].upper() in units:
+ mult = units[unit[0].upper()]
+- all_units = {'B', 'K', 'M', 'G', 'T', 'P',
++ all_units = ['B', 'K', 'M', 'G', 'T', 'P',
+ 'KB', 'MB', 'GB', 'TB', 'PB',
+- 'KIB', 'MIB', 'GIB', 'TIB', 'PIB'}
++ 'KIB', 'MIB', 'GIB', 'TIB', 'PIB']
+ if unit.upper() in all_units:
+ return mult, unit
+ else:
+@@ -198,9 +198,9 @@ def get_device_by_uuid(uuid):
+
+ def get_major_minor(device):
+ real_dev = get_real_device(device)
+- stat = os.stat(real_dev)
+- major = os.major(stat.st_rdev)
+- minor = os.minor(stat.st_rdev)
++ info = os.stat(real_dev)
++ major = os.major(info.st_rdev)
++ minor = os.minor(info.st_rdev)
+ return major, minor
+
+
+@@ -579,7 +579,7 @@ def terminal_size(default=(25, 80)):
+ except:
+ pass
+ if not cr:
+- cr = (25, 80)
++ cr = default
+ return int(cr[1]), int(cr[0])
+
+
+--
+1.8.3.1
+
diff --git a/ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch b/ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch
new file mode 100644
index 0000000..d72922c
--- /dev/null
+++ b/ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch
@@ -0,0 +1,124 @@
+From 7a27177855ea2466d01dd201571f66ddea6c7cc8 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Fri, 17 Jan 2014 16:29:50 +0100
+Subject: [PATCH] ssm: Remove unnecessary /usr/bin/env python
+
+Those are not executable script and should not contain this line.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/__init__.py | 2 --
+ ssmlib/backends/__init__.py | 2 --
+ ssmlib/backends/btrfs.py | 2 --
+ ssmlib/backends/crypt.py | 2 --
+ ssmlib/backends/lvm.py | 2 --
+ ssmlib/backends/md.py | 2 --
+ ssmlib/backends/template.py | 2 --
+ ssmlib/main.py | 2 --
+ ssmlib/misc.py | 2 --
+ ssmlib/problem.py | 2 --
+ 10 files changed, 20 deletions(-)
+
+diff --git a/ssmlib/__init__.py b/ssmlib/__init__.py
+index 0a49de2..7e5e4fd 100644
+--- a/ssmlib/__init__.py
++++ b/ssmlib/__init__.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/backends/__init__.py b/ssmlib/backends/__init__.py
+index 5720e3d..2046e83 100644
+--- a/ssmlib/backends/__init__.py
++++ b/ssmlib/backends/__init__.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
+index acd5446..b0a486f 100644
+--- a/ssmlib/backends/btrfs.py
++++ b/ssmlib/backends/btrfs.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/backends/crypt.py b/ssmlib/backends/crypt.py
+index 8c1b48c..642b905 100644
+--- a/ssmlib/backends/crypt.py
++++ b/ssmlib/backends/crypt.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/backends/lvm.py b/ssmlib/backends/lvm.py
+index 4c7c0ad..4022034 100644
+--- a/ssmlib/backends/lvm.py
++++ b/ssmlib/backends/lvm.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/backends/md.py b/ssmlib/backends/md.py
+index 1e3c6e2..fca2c98 100644
+--- a/ssmlib/backends/md.py
++++ b/ssmlib/backends/md.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2012 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/backends/template.py b/ssmlib/backends/template.py
+index 94f4b40..bf1b6ad 100644
+--- a/ssmlib/backends/template.py
++++ b/ssmlib/backends/template.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2013 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/main.py b/ssmlib/main.py
+index c294aec..5213000 100644
+--- a/ssmlib/main.py
++++ b/ssmlib/main.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/misc.py b/ssmlib/misc.py
+index 77ddc2d..24b7dd9 100644
+--- a/ssmlib/misc.py
++++ b/ssmlib/misc.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2011 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+diff --git a/ssmlib/problem.py b/ssmlib/problem.py
+index dfa6ec6..f8fb4ce 100644
+--- a/ssmlib/problem.py
++++ b/ssmlib/problem.py
+@@ -1,5 +1,3 @@
+-#!/usr/bin/env python
+-#
+ # (C)2012 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+--
+1.8.3.1
+
diff --git a/ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch b/ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch
new file mode 100644
index 0000000..9938a47
--- /dev/null
+++ b/ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch
@@ -0,0 +1,39 @@
+From e2c869393f9c1c0f1c84e1a817f411a2056d820e Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Thu, 16 Jan 2014 14:44:10 +0100
+Subject: [PATCH 08/10] ssm: Suppress backtrace if command failed
+
+Currently when command failed in misc.run(), RuntimeError() exception is
+raised and backtrace is printed to the output. However this is not
+desirable, so use problem.CommandFailed() exception which allows us to
+catch it and print out reasonable error message.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/misc.py | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/ssmlib/misc.py b/ssmlib/misc.py
+index dbfa6b1..3d99c07 100644
+--- a/ssmlib/misc.py
++++ b/ssmlib/misc.py
+@@ -24,6 +24,7 @@ import stat
+ import tempfile
+ import threading
+ import subprocess
++from ssmlib import problem
+
+ # List of temporary mount points which should be cleaned up
+ # before exiting
+@@ -480,7 +481,7 @@ def run(cmd, show_cmd=False, stdout=False, stderr=True, can_fail=False,
+ print output
+ if error is not None:
+ print error
+- raise RuntimeError(err_msg)
++ raise problem.CommandFailed(err_msg)
+
+ if not return_stdout:
+ output = None
+--
+1.8.3.1
+
diff --git a/ssm-0.4-ssm-big-update-no-1.patch b/ssm-0.4-ssm-big-update-no-1.patch
new file mode 100644
index 0000000..11b98f3
--- /dev/null
+++ b/ssm-0.4-ssm-big-update-no-1.patch
@@ -0,0 +1,3126 @@
+diff --git a/Makefile b/Makefile
+index 629a83f..2d887c4 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,3 +1,6 @@
++PYTHONPATH := $(shell pwd)
++export PYTHONPATH
++
+ all: help
+
+ help:
+@@ -53,4 +56,7 @@ source: test clean
+ test:
+ @python test.py
+
++push_html:
++ scp -r doc/_build/singlehtml/* lczerner at shell.sourceforge.net:/home/project-web/storagemanager/htdocs/
++
+ release: git-clean clean check_vars authors spec log docs source
+diff --git a/README b/README
+index ea3d3c3..71b09e8 100644
+--- a/README
++++ b/README
+@@ -162,6 +162,9 @@ In some cases file system has to be mounted in order to resize. This
+ will be handled by **ssm** automatically by mounting the **volume**
+ temporarily.
+
++Note that resizing btrfs subvolume is not supported, only the whole
++file system can be resized.
++
+
+ Check command
+ *************
+@@ -256,8 +259,8 @@ volume
+ Volume in btrfs back-end is actually just btrfs subvolume with the
+ exception of the first volume created on btrfs pool creation, which
+ is the file system itself. Subvolumes can only be created on btrfs
+- file system when the it is mounted, but user does not have to worry
+- about that, since **ssm** will automatically mount the file system
++ file system when it is mounted, but user does not have to worry
++ about that since **ssm** will automatically mount the file system
+ temporarily in order to create a new subvolume.
+
+ Volume name is used as subvolume path in the btrfs file system and
+@@ -270,6 +273,10 @@ volume
+ system is mounted, with the exception of the main btrfs volume -
+ the file system itself.
+
++ Also note that btrfs volumes and subvolumes can not be resized.
++ This is mainly limitation of the btrfs tools which currently does
++ not work reliably.
++
+ New btrfs volume can be created with **create** command.
+
+ snapshot
+@@ -323,10 +330,40 @@ device
+ Crypt backend
+ *************
+
+-Crypt backend in **ssm** is currently limited to only gather the
+-information about encrypted volumes in the system. You can not create
+-or manage encrypted volumes or pools, but it will be extended in the
+-future.
++Crypt backend in **ssm** uses cryptsetup and dm-crypt target to manage
++encrypted volumes. Crypt backend can be used as a regular backend for
++creating encrypted volumes on top of regular block devices, or even
++other volumes (lvm or md volumes for example). Or it can be used to
++create encrypted lvm volumes right away in a single step.
++
++Only volumes can be created with crypt backend. This backend does not
++support pooling and does not require special devices.
++
++pool
++ Crypt backend does not support pooling it is not possible to create
++ crypt pool or add a device into a pool.
++
++volume
++ Volume in crypt backend is the volume created by dm-crypt which
++ represent the data on the original encrypted device in unencrypted
++ form. Crypt backend does not support pooling, so only one device
++ can be used to create crypt volume. It also does not support raid
++ or any device concatenation.
++
++ Currently two modes, or extensions are supported luks and plain.
++ Luks is used by default.For more information about the extensions
++ please see **cryptsetup** manual page.
++
++snapshot
++ Crypt backend does not support snapshotting, however if the
++ encrypted volume is created on top of the lvm volume, the lvm
++ volume itself can be snapshotted. The snapshot can be then opened
++ by using **cryptsetup**. It is possible that this might change in
++ the future so that **ssm** will be able to activate the volume
++ directly without the extra step.
++
++device
++ Crypt backend does not require any special device to be created on.
+
+
+ Environment variables
+diff --git a/bin/ssm b/bin/ssm
+index fc1ce48..baa3c60 100755
+--- a/bin/ssm
++++ b/bin/ssm
+@@ -32,4 +32,5 @@ try:
+ sys.exit("\nRoot privileges required to run this script!\n")
+ sys.exit(main.main())
+ except problem.SsmError, err:
++ print str(err)
+ sys.exit(err.errcode)
+diff --git a/doc/_build/man/ssm.8 b/doc/_build/man/ssm.8
+index 16ac1e8..ab777bf 100644
+--- a/doc/_build/man/ssm.8
++++ b/doc/_build/man/ssm.8
+@@ -1,6 +1,6 @@
+ .\" Man page generated from reStructuredText.
+ .
+-.TH "SSM" "8" "August 07, 2013" "0.4" "System Storage Manager"
++.TH "SSM" "8" "October 02, 2013" "0.4" "System Storage Manager"
+ .SH NAME
+ ssm \- System Storage Manager: a single tool to manage your storage
+ .
+@@ -59,9 +59,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+ ..
+ .SH SYNOPSIS
+ .sp
+-\fBssm\fP [\fB\-h\fP] [\fB\-\-version\fP] [\fB\-v\fP] [\fB\-f\fP] [\fB\-b\fP BACKEND] {check,resize,create,list,add,remove,snapshot,mount} ...
++\fBssm\fP [\fB\-h\fP] [\fB\-\-version\fP] [\fB\-v\fP] [\fB\-f\fP] [\fB\-b\fP BACKEND] [\fB\-n\fP] {check,resize,create,list,add,remove,snapshot,mount} ...
+ .sp
+-\fBssm\fP \fBcreate\fP [\fB\-h\fP] [\fB\-s\fP SIZE] [\fB\-n\fP NAME] [\fB\-\-fstype\fP FSTYPE] [\fB\-r\fP LEVEL] [\fB\-I\fP STRIPESIZE] [\fB\-i\fP STRIPES] [\fB\-p\fP POOL] [\fBdevice\fP [\fBdevice\fP ...]] [mount]
++\fBssm\fP \fBcreate\fP [\fB\-h\fP] [\fB\-s\fP SIZE] [\fB\-n\fP NAME] [\fB\-\-fstype\fP FSTYPE] [\fB\-r\fP LEVEL] [\fB\-I\fP STRIPESIZE] [\fB\-i\fP STRIPES] [\fB\-p\fP POOL] [\fB\-e\fP [{luks,plain}]] [\fBdevice\fP [\fBdevice\fP ...]] [mount]
+ .sp
+ \fBssm\fP \fBlist\fP [\fB\-h\fP] [{volumes,vol,dev,devices,pool,pools,fs,filesystems,snap,snapshots}]
+ .sp
+@@ -110,7 +110,15 @@ or questions.
+ .TP
+ .BI \-b \ BACKEND\fP,\fB \ \-\-backend \ BACKEND
+ Choose backend to use. Currently you can choose from
+-(lvm,btrfs).
++(lvm,btrfs,crypt).
++.TP
++.B \-n\fP,\fB \-\-dry\-run
++Dry run. Do not do anything, just parse the command
++line options and gather system information if
++necessary. Note that with this option ssm will not
++perform all the check as some of them are done by the
++backends themselves. This option is mainly used for
++debugging purposes.
+ .UNINDENT
+ .SH SYSTEM STORAGE MANAGER COMMANDS
+ .SS Introduction
+@@ -120,7 +128,7 @@ line as a first argument to the ssm. They all have specific use and its own
+ arguments, but global ssm arguments are propagated to all commands.
+ .SS Create command
+ .sp
+-\fBssm\fP \fBcreate\fP [\fB\-h\fP] [\fB\-s\fP SIZE] [\fB\-n\fP NAME] [\fB\-\-fstype\fP FSTYPE] [\fB\-r\fP LEVEL] [\fB\-I\fP STRIPESIZE] [\fB\-i\fP STRIPES] [\fB\-p\fP POOL] [\fBdevice\fP [\fBdevice\fP ...]] [mount]
++\fBssm\fP \fBcreate\fP [\fB\-h\fP] [\fB\-s\fP SIZE] [\fB\-n\fP NAME] [\fB\-\-fstype\fP FSTYPE] [\fB\-r\fP LEVEL] [\fB\-I\fP STRIPESIZE] [\fB\-i\fP STRIPES] [\fB\-p\fP POOL] [\fB\-e\fP [{luks,plain}]] [\fBdevice\fP [\fBdevice\fP ...]] [mount]
+ .sp
+ This command creates a new volume with defined parameters. If \fBdevice\fP is
+ provided it will be used to create a volume, hence it will be added into the
+@@ -195,6 +203,12 @@ you have to specify RAID level as well.
+ .BI \-p \ POOL\fP,\fB \ \-\-pool \ POOL
+ Pool to use to create the new volume.
+ .UNINDENT
++.INDENT 0.0
++.TP
++.B \-e [{luks,plain}], \-\-encrypt [{luks,plain}]
++Create encrpted volume. Extension to use can be
++specified.
++.UNINDENT
+ .SS List command
+ .sp
+ \fBssm\fP \fBlist\fP [\fB\-h\fP] [{volumes,vol,dev,devices,pool,pools,fs,filesystems,snap,snapshots}]
+@@ -280,6 +294,9 @@ ask you whether you want to remove it from the original pool.
+ .sp
+ In some cases file system has to be mounted in order to resize. This will be
+ handled by \fBssm\fP automatically by mounting the \fBvolume\fP temporarily.
++.sp
++Note that resizing btrfs subvolume is not supported, only the whole file
++system can be resized.
+ .INDENT 0.0
+ .TP
+ .B \-h\fP,\fB \-\-help
+@@ -435,8 +452,8 @@ devices specified and non existing pool name.
+ Volume in btrfs back\-end is actually just btrfs subvolume with the
+ exception of the first volume created on btrfs pool creation, which is
+ the file system itself. Subvolumes can only be created on btrfs file
+-system when the it is mounted, but user does not have to
+-worry about that, since \fBssm\fP will automatically mount the file
++system when it is mounted, but user does not have to
++worry about that since \fBssm\fP will automatically mount the file
+ system temporarily in order to create a new subvolume.
+ .sp
+ Volume name is used as subvolume path in the btrfs file system and every
+@@ -449,6 +466,10 @@ Btrfs volumes are only shown in the \fIlist\fP output, when the file system is
+ mounted, with the exception of the main btrfs volume \- the file system
+ itself.
+ .sp
++Also note that btrfs volumes and subvolumes can not be resized. This is
++mainly limitation of the btrfs tools which currently does not work
++reliably.
++.sp
+ New btrfs volume can be created with \fBcreate\fP command.
+ .TP
+ .B snapshot
+@@ -498,9 +519,41 @@ Lvm requires \fIphysical device\fP to be created on the device, but with
+ .UNINDENT
+ .SS Crypt backend
+ .sp
+-Crypt backend in \fBssm\fP is currently limited to only gather the information
+-about encrypted volumes in the system. You can not create or manage encrypted
+-volumes or pools, but it will be extended in the future.
++Crypt backend in \fBssm\fP uses cryptsetup and dm\-crypt target to manage
++encrypted volumes. Crypt backend can be used as a regular backend for
++creating encrypted volumes on top of regular block devices, or even other
++volumes (lvm or md volumes for example). Or it can be used to create
++encrypted lvm volumes right away in a single step.
++.sp
++Only volumes can be created with crypt backend. This backend does not
++support pooling and does not require special devices.
++.INDENT 0.0
++.TP
++.B pool
++Crypt backend does not support pooling it is not possible to create
++crypt pool or add a device into a pool.
++.TP
++.B volume
++Volume in crypt backend is the volume created by dm\-crypt which
++represent the data on the original encrypted device in unencrypted form.
++Crypt backend does not support pooling, so only one device can be used
++to create crypt volume. It also does not support raid or any device
++concatenation.
++.sp
++Currently two modes, or extensions are supported luks and plain. Luks
++is used by default.For more information about the extensions please see
++\fBcryptsetup\fP manual page.
++.TP
++.B snapshot
++Crypt backend does not support snapshotting, however if the encrypted
++volume is created on top of the lvm volume, the lvm volume itself can
++be snapshotted. The snapshot can be then opened by using \fBcryptsetup\fP\&.
++It is possible that this might change in the future so that \fBssm\fP will
++be able to activate the volume directly without the extra step.
++.TP
++.B device
++Crypt backend does not require any special device to be created on.
++.UNINDENT
+ .SS MD backend
+ .sp
+ MD backend in \fBssm\fP is currently limited to only gather the information
+diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
+index 04e405c..a4aedda 100644
+--- a/ssmlib/backends/btrfs.py
++++ b/ssmlib/backends/btrfs.py
+@@ -23,6 +23,7 @@ import sys
+ import datetime
+ from ssmlib import misc
+ from ssmlib import problem
++from ssmlib.backends import template
+
+ __all__ = ["BtrfsVolume", "BtrfsPool", "BtrfsDev"]
+
+@@ -56,12 +57,11 @@ def get_btrfs_version():
+ BTRFS_VERSION = get_btrfs_version()
+
+
+-class Btrfs(object):
++class Btrfs(template.Backend):
+
+- def __init__(self, options, data=None):
++ def __init__(self, *args, **kwargs):
++ super(Btrfs, self).__init__(*args, **kwargs)
+ self.type = 'btrfs'
+- self.data = data or {}
+- self.options = options
+ self.default_pool_name = SSM_BTRFS_DEFAULT_POOL
+ self._vol = {}
+ self._pool = {}
+@@ -69,13 +69,12 @@ class Btrfs(object):
+ self._snap = {}
+ self._subvolumes = {}
+ self._binary = misc.check_binary('btrfs')
+- self.problem = problem.ProblemSet(options)
+ self.modified_list_version = True
+
+ if not self._binary:
+ return
+
+- self.mounts = misc.get_mounts('/dev/')
++ self.mounts = misc.get_mounts('btrfs')
+ command = ['btrfs', 'filesystem', 'show']
+ self.output = misc.run(command, stderr=False)[1]
+
+@@ -107,12 +106,12 @@ class Btrfs(object):
+ pool['uuid'] = vol['uuid'] = uuid
+
+ try:
+- fallback = False
+ vol['real_dev'] = misc.get_device_by_uuid(uuid)
+
+ if vol['real_dev'] in self.mounts:
+ pool['mount'] = self.mounts[vol['real_dev']]['mp']
+ vol['mount'] = self.mounts[vol['real_dev']]['mp']
++
+ else:
+ for dev_i in self.mounts:
+ found = re.findall(r'{0}:/.*'.format(vol['real_dev']), dev_i)
+@@ -121,7 +120,6 @@ class Btrfs(object):
+ break
+ except OSError:
+ # udev is "hard-to-work-with" sometimes so this is fallback
+- fallback = True
+ vol['real_dev'] = ""
+
+ if label != 'none':
+@@ -140,7 +138,7 @@ class Btrfs(object):
+ dev['pool_name'] = pool_name
+
+ # Fallback in case we could not find real_dev by uuid
+- if fallback and 'mount' not in pool:
++ if 'mount' not in pool:
+ if dev['dev_name'] in self.mounts:
+ pool['mount'] = self.mounts[dev['dev_name']]['mp']
+ vol['real_dev'] = dev['dev_name']
+@@ -153,6 +151,7 @@ class Btrfs(object):
+ found = re.findall(r'{0}:/.*'.format(dev['dev_name']), dev_i)
+ if found:
+ pool['mount'] = self.mounts[found[0]]['mp']
++ vol['real_dev'] = found[0].split(':')[0]
+ break
+
+ dev_used = get_real_number(array[5])
+@@ -313,26 +312,18 @@ class Btrfs(object):
+ self._pool[pool['pool_name']] = pool
+ self._vol[vol['dev_name']] = vol
+
+- def __iter__(self):
+- for item in sorted(self.data.iterkeys()):
+- yield item
+-
+- def __getitem__(self, key):
+- if key in self.data.iterkeys():
+- return self.data[key]
+-
+ def _remove_filesystem(self, name):
+ if 'mount' in self._vol[name]:
+ if self.problem.check(self.problem.FS_MOUNTED,
+ [name, self._vol[name]['mount']]):
+- misc.do_umount(self._vol[name]['mount'])
++ misc.do_umount(self._vol[name]['real_dev'], all_targets=True)
+ for dev in self._dev.itervalues():
+ if dev['pool_name'] != name:
+ continue
+ misc.wipefs(dev['dev_name'], 'btrfs')
+
+
+-class BtrfsVolume(Btrfs):
++class BtrfsVolume(Btrfs, template.BackendVolume):
+
+ def __init__(self, *args, **kwargs):
+ super(BtrfsVolume, self).__init__(*args, **kwargs)
+@@ -374,11 +365,18 @@ class BtrfsVolume(Btrfs):
+ else:
+ self._remove_filesystem(vol)
+
++ def check(self, vol):
++ vol = self.data[vol]
++ return self.run_btrfs(['check', vol['real_dev']])[0]
++
+ def resize(self, vol, size, resize_fs=True):
+ vol = self.data[vol]
+ if 'mount' not in vol:
+ tmp = misc.temp_mount("UUID={0}".format(vol['uuid']))
+ vol['mount'] = tmp
++ if 'subvolume' in vol and vol['subvolume'] is True:
++ self.problem.check(self.problem.NOT_SUPPORTED,
++ 'Resizing btrfs subvolume')
+ command = ['filesystem', 'resize', str(int(size)) + "K", vol['mount']]
+ self.run_btrfs(command)
+
+@@ -402,7 +400,7 @@ class BtrfsVolume(Btrfs):
+ self.run_btrfs(command)
+
+
+-class BtrfsDev(Btrfs):
++class BtrfsDev(Btrfs, template.BackendDevice):
+
+ def __init__(self, *args, **kwargs):
+ super(BtrfsDev, self).__init__(*args, **kwargs)
+@@ -416,7 +414,7 @@ class BtrfsDev(Btrfs):
+ "achieve by removing {0}".format(devices))
+
+
+-class BtrfsPool(Btrfs):
++class BtrfsPool(Btrfs, template.BackendPool):
+
+ def __init__(self, *args, **kwargs):
+ super(BtrfsPool, self).__init__(*args, **kwargs)
+@@ -425,7 +423,22 @@ class BtrfsPool(Btrfs):
+ else:
+ self.data = self._pool
+
+- def _create_filesystem(self, pool, name, devs, size=None, raid=None):
++ def _can_btrfs_force(self):
++ """
++ This is just ridiculous. Unfortunately btrfs tools usually change
++ behaviour and options without bumping version number. So we have
++ to check whether btrfs allows to 'force' file system creation.
++ """
++ command=['mkfs.btrfs', '-f']
++ output = misc.run(command, can_fail=True)[1]
++ found = re.search('invalid option', output)
++ if found:
++ return False
++ else:
++ return True
++
++ def _create_filesystem(self, pool, name, devs, size=None, options=None):
++ options = options or {}
+ if not devs:
+ raise Exception("To create btrfs volume, some devices must be " +
+ "provided")
+@@ -434,12 +447,12 @@ class BtrfsPool(Btrfs):
+ self.problem.check(self.problem.TOOL_MISSING, 'mkfs.btrfs')
+ command = ['mkfs.btrfs', '-L', name]
+
+- if raid:
+- if raid['level'] == '0':
++ if 'raid' in options:
++ if options['raid'] == '0':
+ command.extend(['-m', 'raid0', '-d', 'raid0'])
+- elif raid['level'] == '1':
++ elif options['raid'] == '1':
+ command.extend(['-m', 'raid1', '-d', 'raid1'])
+- elif raid['level'] == '10':
++ elif options['raid'] == '10':
+ command.extend(['-m', 'raid10', '-d', 'raid10'])
+ else:
+ raise Exception("Btrfs backed currently does not support " +
+@@ -455,7 +468,8 @@ class BtrfsPool(Btrfs):
+ # have tried to remove the device from the respective pool already.
+ # So at this point there should not be any useful signatures to
+ # speak of. However as I mentioned btrfs is broken, so force it.
+- command.extend(['-f'])
++ if self._can_btrfs_force():
++ command.extend(['-f'])
+ command.extend(devs)
+ misc.run(command, stdout=True)
+ misc.send_udev_event(devs[0], "change")
+@@ -507,10 +521,11 @@ class BtrfsPool(Btrfs):
+ self._remove_filesystem(pool)
+
+ def create(self, pool, size=None, name=None, devs=None,
+- raid=None):
++ options=None):
++ options = options or {}
+ if pool in self._pool:
+ vol = None
+- if size or raid:
++ if size or 'raid' in options:
+ self.problem.warn("Only name, volume name and pool name " +
+ "can be specified when creating btrfs " +
+ "subvolume, the rest will be ignored")
+@@ -535,7 +550,7 @@ class BtrfsPool(Btrfs):
+ if name:
+ self.problem.warn("Creating new pool. Argument (--name " +
+ "{0}) will be ignored!".format(name))
+- vol = self._create_filesystem(pool, pool, devs, size, raid)
++ vol = self._create_filesystem(pool, pool, devs, size, options)
+ return vol
+
+
+diff --git a/ssmlib/backends/crypt.py b/ssmlib/backends/crypt.py
+index 3c44f58..4aad266 100644
+--- a/ssmlib/backends/crypt.py
++++ b/ssmlib/backends/crypt.py
+@@ -19,36 +19,147 @@
+
+ import re
+ import os
++import stat
+ from ssmlib import misc
+ from ssmlib import problem
++from ssmlib.backends import template
+
+ __all__ = ["DmCryptVolume"]
+
++SUPPORTED_CRYPT = ['luks', 'plain']
++CRYPT_SIGNATURES = ['crypto_LUKS']
++CRYPT_DEFAULT_EXTENSION = "luks"
++
+ try:
+ SSM_CRYPT_DEFAULT_POOL = os.environ['SSM_CRYPT_DEFAULT_POOL']
+ except KeyError:
+ SSM_CRYPT_DEFAULT_POOL = "crypt_pool"
+
+ try:
+- DM_DEV_DIR = os.environ['DM_DEV_DIR']
++ SSM_CRYPT_DEFAULT_VOL_PREFIX = os.environ['SSM_CRYPT_DEFAULT_VOL_PREFIX']
+ except KeyError:
+- DM_DEV_DIR = "/dev"
+-
+-
+-class DmCryptVolume(object):
+-
+- def __init__(self, options, data=None):
++ SSM_CRYPT_DEFAULT_VOL_PREFIX = "encrypted"
++
++# cryptsetup against my expectations does not take into account
++# DM_DEV_DIR so set it to /dev pernamently for now.
++#try:
++# DM_DEV_DIR = os.environ['DM_DEV_DIR']
++#except KeyError:
++# DM_DEV_DIR = "/dev"
++DM_DEV_DIR = "/dev"
++MAX_DEVS = 999
++
++def get_cryptsetup_version():
++ try:
++ output = misc.run(['cryptsetup', '--version'], can_fail=True)[1]
++ version = map(int, output.strip().split()[-1].split('.', 3))
++ except (OSError, AttributeError):
++ version = [0, 0, 0]
++ return version
++
++CRYPTSETUP_VERSION = get_cryptsetup_version()
++
++class DmObject(template.Backend):
++ def __init__(self, *args, **kwargs):
++ super(DmObject, self).__init__(*args, **kwargs)
+ self.type = 'crypt'
+- self.data = data or {}
+- self.output = None
+- self.options = options
+ self.mounts = misc.get_mounts('{0}/mapper'.format(DM_DEV_DIR))
+ self.default_pool_name = SSM_CRYPT_DEFAULT_POOL
+- self.problem = problem.ProblemSet(options)
+
+ if not misc.check_binary('dmsetup') or \
+ not misc.check_binary('cryptsetup'):
+ return
++
++ def run_cryptsetup(self, command, stdout=True):
++ if not misc.check_binary('cryptsetup'):
++ self.problem.check(self.problem.TOOL_MISSING, 'cryptsetup')
++ command.insert(0, "cryptsetup")
++ return misc.run(command, stdout=stdout)
++
++
++class DmCryptPool(DmObject, template.BackendPool):
++ def __init__(self, *args, **kwargs):
++ super(DmCryptPool, self).__init__(*args, **kwargs)
++ '''
++ pool = {'pool_name': self.default_pool_name,
++ 'type': 'crypt',
++ 'dev_count': '0',
++ 'pool_free': '0',
++ 'pool_used': '0',
++ 'pool_size': '0',
++ 'hide': True}
++ self.data[self.default_pool_name] = pool
++ '''
++
++
++ def create(self, pool, size=None, name=None, devs=None,
++ options=None):
++
++ if CRYPTSETUP_VERSION < [1, 6, 0]:
++ msg = "You need at least cryptsetup version " + \
++ "{0}. Creating encrypted volumes".format('1.6.0')
++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
++ options = options or {}
++ if 'encrypt' in options:
++ if options['encrypt'] is True:
++ options['encrypt'] = CRYPT_DEFAULT_EXTENSION
++ if options['encrypt'] not in SUPPORTED_CRYPT:
++ self.problem.not_supported("Extension "
++ "\'{0}\'".format(options['encrypt']))
++ else:
++ # So the options is not crypt specific. It's ok, just use defaults
++ options['encrypt'] = CRYPT_DEFAULT_EXTENSION
++
++ if len(devs) > 1:
++ self.problem.not_supported("Device concatenation" +
++ " with \"crypt\" backend")
++ if not name:
++ name = self._generate_devname()
++ device = devs[0]
++ args = []
++ command = []
++ if self.options.verbose:
++ args.append('-v')
++ else:
++ args.append('-q')
++ if options['encrypt'] == "luks":
++ command.extend(args)
++ if self.options.force:
++ command.append('--force-password')
++ if self.options.interactive:
++ command.append('-y')
++ command.extend(['luksFormat', device])
++ self.run_cryptsetup(command)
++ command = []
++ command.extend(args)
++ command.append('open')
++ if size:
++ # Size is in KiB but cryptsetup accepts it in 512 byte blocks
++ size = str(float(size) * 2).split('.')[0]
++ command.extend(['--size', size])
++ command.extend(['--type', options['encrypt'], device, name])
++ self.run_cryptsetup(command)
++ return "{0}/mapper/{1}".format(DM_DEV_DIR, name)
++
++ def _generate_devname(self):
++ for i in range(1, MAX_DEVS):
++ name = "{0}{1:0>{align}}".format(SSM_CRYPT_DEFAULT_VOL_PREFIX, i,
++ align=len(str(MAX_DEVS)))
++ path = "{0}/mapper/{1}".format(DM_DEV_DIR, name)
++ try:
++ if stat.S_ISBLK(os.stat(path).st_mode):
++ continue
++ except OSError:
++ pass
++ return name
++ self.problem.error("Can not find proper device name. Specify one!")
++
++
++class DmCryptVolume(DmObject, template.BackendVolume):
++
++ def __init__(self, *args, **kwargs):
++ super(DmCryptVolume, self).__init__(*args, **kwargs)
++
+ command = ['dmsetup', 'table']
+ self.output = misc.run(command, stderr=False)[1]
+ for line in self.output.split("\n"):
+@@ -63,9 +174,9 @@ class DmCryptVolume(object):
+ devname = re.sub(":$", "",
+ "{0}/mapper/{1}".format(DM_DEV_DIR, array[0]))
+ dm['dm_name'] = devname
+- dm['pool_name'] = 'dm-crypt'
+- dm['dev_name'] = misc.get_real_device(devname)
+- dm['real_dev'] = dm['dev_name']
++ dm['pool_name'] = self.default_pool_name
++ dm['dev_name'] = devname
++ dm['real_dev'] = misc.get_real_device(devname)
+ if dm['real_dev'] in self.mounts:
+ dm['mount'] = self.mounts[dm['real_dev']]['mp']
+
+@@ -78,12 +189,6 @@ class DmCryptVolume(object):
+ self._parse_cryptsetup(command, dm)
+ self.data[dm['dev_name']] = dm
+
+- def run_cryptsetup(self, command, stdout=True):
+- if not self._binary:
+- self.problem.check(self.problem.TOOL_MISSING, 'cryptsetup')
+- command.insert(0, "cryptsetup")
+- return misc.run(command, stdout=stdout)
+-
+ def _parse_cryptsetup(self, cmd, dm):
+ self.output = misc.run(cmd, stderr=False)[1]
+ for line in self.output.split("\n"):
+@@ -97,19 +202,52 @@ class DmCryptVolume(object):
+ elif array[0].strip() == 'device:':
+ dm['crypt_device'] = array[1]
+
++ def __getitem__(self, name):
++ if name in self.data.iterkeys():
++ return self.data[name]
++ device = name
++ if not os.path.exists(name):
++ device = DM_DEV_DIR + "/" + name
++ if not os.path.exists(device):
++ return None
++ device = misc.get_real_device(device)
++ if device in self.data.iterkeys():
++ return self.data[device]
++ return None
++
+ def remove(self, dm):
++ vol = self[dm]
++ if 'mount' in vol:
++ if self.problem.check(self.problem.FS_MOUNTED,
++ [vol['dev_name'], vol['mount']]):
++ misc.do_umount(vol['mount'])
+ command = ['remove', dm]
+ self.run_cryptsetup(command)
++ misc.wipefs(vol['crypt_device'], CRYPT_SIGNATURES)
+
+ def resize(self, dm, size, resize_fs=True):
+ size = str(int(size) * 2)
+ command = ['resize', '--size', size, dm]
+ self.run_cryptsetup(command)
+
+- def __iter__(self):
+- for item in sorted(self.data.iterkeys()):
+- yield item
+
+- def __getitem__(self, key):
+- if key in self.data.iterkeys():
+- return self.data[key]
++class DmCryptDevice(DmObject, template.BackendDevice):
++
++ def __init__(self, *args, **kwargs):
++ super(DmCryptDevice, self).__init__(*args, **kwargs)
++
++ for line in misc.get_partitions():
++ device = {}
++ devname = "/dev/" + line[3]
++ signature = misc.get_signature(devname)
++ if misc.get_signature(devname) in CRYPT_SIGNATURES:
++ device['hide'] = False
++ device['dev_name'] = devname
++ device['pool_name'] = self.default_pool_name
++ device['dev_free'] = '0'
++ device['dev_used'] = str(misc.get_device_size(devname))
++ self.data[devname] = device
++
++
++ def remove(self, devices):
++ misc.wipefs(device, CRYPT_SIGNATURES)
+diff --git a/ssmlib/backends/lvm.py b/ssmlib/backends/lvm.py
+index 9cc8079..955c705 100644
+--- a/ssmlib/backends/lvm.py
++++ b/ssmlib/backends/lvm.py
+@@ -17,11 +17,13 @@
+
+ # lvm module for System Storage Manager
+
++import re
+ import os
+ import stat
+ import datetime
+ from ssmlib import misc
+ from ssmlib import problem
++from ssmlib.backends import template
+
+ __all__ = ["PvsInfo", "VgsInfo", "LvsInfo"]
+
+@@ -37,17 +39,32 @@ except KeyError:
+ MAX_LVS = 999
+
+
+-class LvmInfo(object):
++def get_lvm_version():
++ try:
++ output = misc.run(['lvm', 'version'], can_fail=True)[1]
++ output = output.strip().split("\n")
++ pattern = re.compile("LVM version:")
++ for line in output:
++ if pattern.match(line.strip()):
++ match = " ".join(line.split())
++ tmp = re.search('(?<=LVM version: )\d+\.\d+\.\d+',
++ match).group(0)
++ version = map(int, tmp.split(".", 3))
++ except (OSError, AttributeError):
++ version = [0, 0, 0]
++ return version
+
+- def __init__(self, options, data=None):
++LVM_VERSION = get_lvm_version()
++
++
++class LvmInfo(template.Backend):
++
++ def __init__(self, *args, **kwargs):
++ super(LvmInfo, self).__init__(*args, **kwargs)
+ self.type = 'lvm'
+- self.data = data or {}
+ self.attrs = []
+- self.output = None
+- self.options = options
+ self.binary = misc.check_binary('lvm')
+ self.default_pool_name = SSM_LVM_DEFAULT_POOL
+- self.problem = problem.ProblemSet(options)
+
+ def run_lvm(self, command, noforce=False):
+ if not self.binary:
+@@ -59,9 +76,6 @@ class LvmInfo(object):
+ command.insert(0, "lvm")
+ misc.run(command, stdout=True)
+
+- def __str__(self):
+- return self.output
+-
+ def _data_index(self, row):
+ return row.values()[len(row.values()) - 1]
+
+@@ -86,16 +100,16 @@ class LvmInfo(object):
+ def _fill_aditional_info(self, row):
+ pass
+
+- def __iter__(self):
+- for item in sorted(self.data.iterkeys()):
+- yield item
++ def supported_since(self, version, string):
++ if version > LVM_VERSION:
++ msg = "ERROR: You need at least lvm version " + \
++ "{0}. Feature \"{1}\"".format(".".join(map(str, version)),
++ string)
++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
++ return True
+
+- def __getitem__(self, key):
+- if key in self.data.iterkeys():
+- return self.data[key]
+
+-
+-class VgsInfo(LvmInfo):
++class VgsInfo(LvmInfo, template.BackendPool):
+
+ def __init__(self, *args, **kwargs):
+ super(VgsInfo, self).__init__(*args, **kwargs)
+@@ -149,40 +163,85 @@ class VgsInfo(LvmInfo):
+ self.run_lvm(command)
+
+ def create(self, vg, size=None, name=None, devs=None,
+- raid=None):
++ options=None):
++ options = options or {}
+ devices = devs or []
+ command = ['lvcreate', vg]
+- if size:
+- command.extend(['-L', size + 'K'])
+- else:
+- if len(devices) > 0:
+- size = "100%PVS"
+- else:
+- size = "100%FREE"
+- command.extend(['-l', size])
+
+ if name:
+ lvname = name
+ else:
+ lvname = self._generate_lvname(vg)
+
++ if size:
++ command.extend(['-L', size + 'K'])
++ else:
++ if len(devices) > 0:
++ tmp = "100%PVS"
++ else:
++ tmp = "100%FREE"
++ command.extend(['-l', tmp])
++
+ command.extend(['-n', lvname.rpartition("/")[-1]])
+
+- if raid:
+- if raid['level'] == '0':
+- if not raid['stripesize']:
+- raid['stripesize'] = "64"
+- if not raid['stripes'] and len(devices) > 0:
+- raid['stripes'] = str(len(devices))
+- if not raid['stripes']:
++ if 'raid' in options:
++ if options['raid'] == '0':
++ if not options['stripesize']:
++ options['stripesize'] = "64"
++ if not options['stripes'] and len(devices) > 0:
++ options['stripes'] = str(len(devices))
++ if not options['stripes']:
+ self.problem.error("Devices or number of " +
+ "stripes should be defined!")
+- if raid['stripesize']:
+- command.extend(['-I', raid['stripesize']])
+- if raid['stripes']:
+- command.extend(['-i', raid['stripes']])
++ if options['stripesize']:
++ command.extend(['-I', options['stripesize']])
++ if options['stripes']:
++ command.extend(['-i', options['stripes']])
++ elif options['raid'] == '1' and \
++ self.supported_since([2,2,89],"raid1"):
++ if options['stripesize'] or options['stripes']:
++ msg = "ERROR: Specifying stripe size or number of " + \
++ "stripes when creating raid1 volume with lvm backend"
++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
++ # Unfortunately 50%PVS does not work here because it does not
++ # take in account metadata needed to create mirrored volume.
++ # Using 49%PVS is not viable either because it will cut off
++ # a lot of potential storage. So we'll require to specify
++ # size in this case.
++ if not size:
++ msg = "ERROR: Creating raid1 with lvm backend without " + \
++ "specifying size"
++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
++ command.extend(["--type", "raid1"])
++ elif options['raid'] == '10' and \
++ self.supported_since([2,2,98],"raid10"):
++ if not options['stripesize']:
++ options['stripesize'] = "64"
++ if not options['stripes'] and len(devices) > 0:
++ if len(devices) < 4:
++ self.problem.error("Number of devices should be at " +
++ "least 4 in raid 10 setup")
++ if len(devices) % 2 != 0:
++ self.problem.error("Number of devices should be " +
++ "even in raid 10 setup")
++ options['stripes'] = str(len(devices)/2)
++ if not options['stripes']:
++ self.problem.error("Devices or number of " +
++ "stripes should be defined")
++ if int(options['stripes']) < 2:
++ self.problem.error("Number of stripes should be at " +
++ "least 2 in raid 10 setup")
++ if options['stripesize']:
++ command.extend(['-I', options['stripesize']])
++ if options['stripes']:
++ command.extend(['-i', options['stripes']])
++ if not size:
++ msg = "ERROR: Creating raid10 with lvm backend without " + \
++ "specifying size"
++ self.problem.check(self.problem.NOT_SUPPORTED, msg)
++ command.extend(["--type", "raid10"])
+ else:
+- self.problem.not_supported("RAID level {0}".format(raid['level']) +
++ self.problem.not_supported("RAID level {0}".format(options['raid']) +
+ " with \"lvm\" backend")
+
+ command.extend(devices)
+@@ -190,7 +249,7 @@ class VgsInfo(LvmInfo):
+ return "{0}/{1}/{2}".format(DM_DEV_DIR, vg, lvname)
+
+
+-class PvsInfo(LvmInfo):
++class PvsInfo(LvmInfo, template.BackendDevice):
+
+ def __init__(self, *args, **kwargs):
+ super(PvsInfo, self).__init__(*args, **kwargs)
+@@ -221,7 +280,7 @@ class PvsInfo(LvmInfo):
+ self.run_lvm(command)
+
+
+-class LvsInfo(LvmInfo):
++class LvsInfo(LvmInfo, template.BackendVolume):
+
+ def __init__(self, *args, **kwargs):
+ super(LvsInfo, self).__init__(*args, **kwargs)
+diff --git a/ssmlib/backends/md.py b/ssmlib/backends/md.py
+index 9e902d7..f33243c 100644
+--- a/ssmlib/backends/md.py
++++ b/ssmlib/backends/md.py
+@@ -23,6 +23,7 @@ import socket
+ import datetime
+ from ssmlib import misc
+ from ssmlib import problem
++from ssmlib.backends import template
+
+ try:
+ SSM_DM_DEFAULT_POOL = os.environ['SSM_DM_DEFAULT_POOL']
+@@ -32,15 +33,14 @@ except KeyError:
+ MDADM = "mdadm"
+
+
+-class MdRaid(object):
++class MdRaid(template.Backend):
+
+- def __init__(self, options, data=None):
++ def __init__(self, *args, **kwargs):
++ super(MdRaid, self).__init__(*args, **kwargs)
+ self.type = 'dm'
+- self.data = data or {}
+ self._vol = {}
+ self._pool = {}
+ self._dev = {}
+- self.options = options
+ self.hostname = socket.gethostname()
+ self._binary = misc.check_binary(MDADM)
+ self.default_pool_name = SSM_DM_DEFAULT_POOL
+@@ -50,7 +50,6 @@ class MdRaid(object):
+ if not self._binary:
+ return
+
+- self.problem = problem.ProblemSet(options)
+ self.mounts = misc.get_mounts('/dev/md')
+
+ mdnumber = misc.get_dmnumber("md")
+@@ -109,16 +108,8 @@ class MdRaid(object):
+ command.insert(0, MDADM)
+ return misc.run(command, stdout=True)
+
+- def __iter__(self):
+- for item in sorted(self.data.iterkeys()):
+- yield item
+-
+- def __getitem__(self, key):
+- if key in self.data.iterkeys():
+- return self.data[key]
+-
+
+-class MdRaidVolume(MdRaid):
++class MdRaidVolume(MdRaid, template.BackendVolume):
+
+ def __init__(self, *args, **kwargs):
+ super(MdRaidVolume, self).__init__(*args, **kwargs)
+@@ -135,7 +126,7 @@ class MdRaidVolume(MdRaid):
+ self.problem.not_supported("Resizing with \"md\" backend")
+
+
+-class MdRaidDevice(MdRaid):
++class MdRaidDevice(MdRaid, template.BackendDevice):
+
+ def __init__(self, *args, **kwargs):
+ super(MdRaidDevice, self).__init__(*args, **kwargs)
+diff --git a/ssmlib/backends/template.py b/ssmlib/backends/template.py
+new file mode 100644
+index 0000000..94f4b40
+--- /dev/null
++++ b/ssmlib/backends/template.py
+@@ -0,0 +1,95 @@
++#!/usr/bin/env python
++#
++# (C)2013 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++#
++# This program is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++# template module for System Storage Manager contains template classes
++# to use when creating new backend
++
++import os
++from ssmlib import misc
++from ssmlib import problem
++
++__all__ = ["Backend", "BackendPool", "BackendVolume", "BackendDevice"]
++
++try:
++ SSM_TEMPLATE_DEFAULT_POOL = os.environ['SSM_TEMPLATE_DEFAULT_POOL']
++except KeyError:
++ SSM_TEMPLATE_DEFAULT_POOL = "template_pool"
++
++
++class Backend(object):
++ def __init__(self, options, data=None):
++ self.type = 'template'
++ self.data = data or {}
++ self.options = options
++ self.output = None
++ self.default_pool_name = SSM_TEMPLATE_DEFAULT_POOL
++ self.problem = problem.ProblemSet(options)
++
++ def __str__(self):
++ return repr(self.data)
++
++ def __iter__(self):
++ for item in sorted(self.data.iterkeys()):
++ yield item
++
++ def __getitem__(self, key):
++ if key in self.data.iterkeys():
++ return self.data[key]
++
++
++class BackendPool(Backend):
++ def __init__(self, *args, **kwargs):
++ super(BackendPool, self).__init__(*args, **kwargs)
++
++ def reduce(self, pool, device):
++ self.problem.check(self.problem.NOT_SUPPORTED,
++ "Reducing pool with {0} backend".format(self.type))
++
++ def new(self, pool, devices):
++ self.problem.check(self.problem.NOT_SUPPORTED,
++ "Creating new pool with {0} backend".format(self.type))
++
++ def extend(self, pool, devices):
++ self.problem.check(self.problem.NOT_SUPPORTED,
++ "Extending pool with {0} backend".format(self.type))
++
++ def remove(self, pool):
++ self.problem.check(self.problem.NOT_SUPPORTED,
++ "Removing pool with {0} backend".format(self.type))
++
++ def create(self, pool, size=None, name=None, devs=None,
++ options=None):
++ self.problem.check(self.problem.NOT_IMPLEMENTED,
++ "Creating volume with {0} backend".format(self.type))
++
++
++class BackendVolume(Backend):
++ def __init__(self, *args, **kwargs):
++ super(BackendVolume, self).__init__(*args, **kwargs)
++
++ def remove(self, volume):
++ self.problem.check(self.problem.NOT_SUPPORTED,
++ "Removing volume with {0} backend".format(self.type))
++
++
++class BackendDevice(Backend):
++ def __init__(self, *args, **kwargs):
++ super(BackendDevice, self).__init__(*args, **kwargs)
++
++ def remove(self, devices):
++ self.problem.check(self.problem.NOT_SUPPORTED,
++ "Removing device with {0} backend".format(self.type))
+diff --git a/ssmlib/main.py b/ssmlib/main.py
+index ef9232d..7aab312 100644
+--- a/ssmlib/main.py
++++ b/ssmlib/main.py
+@@ -30,7 +30,7 @@ from ssmlib.backends import lvm, crypt, btrfs, md
+
+ EXTN = ['ext2', 'ext3', 'ext4']
+ SUPPORTED_FS = ['xfs', 'btrfs'] + EXTN
+-SUPPORTED_BACKENDS = ['lvm', 'btrfs']
++SUPPORTED_BACKENDS = ['lvm', 'btrfs', 'crypt']
+ SUPPORTED_RAID = ['0', '1', '10']
+ os.environ['LC_NUMERIC'] = "C"
+
+@@ -222,7 +222,7 @@ class FsInfo(object):
+ raise Exception("File system on {0} is not ".format(self.device) +
+ "clean, I will not attempt to resize it. Please," +
+ "fix the problem first.")
+- misc.run(command, stdout=True)
++ return misc.run(command, stdout=True)[0]
+
+ def xfs_get_info(self, dev):
+ command = ["xfs_db", "-r", "-c", "sb", "-c", "print", dev]
+@@ -341,6 +341,11 @@ class DeviceInfo(object):
+ dev['mount'] = "PARTITIONED"
+ dev['type'] = 'disk'
+
++ def remove(self, device):
++ PR.error("It is not clear what do you want " +
++ "to achieve by removing " +
++ "{0}!".format(device))
++
+ def set_globals(self, options):
+ self.options = options
+
+@@ -410,6 +415,9 @@ class Item(object):
+ else:
+ name = self.data['dev_name']
+ fs = FsInfo(name, self.obj.options)
++ if 'fs_type' not in fs.data:
++ # Not a file system
++ return
+ try:
+ fs.mounted = self.data['mount']
+ except KeyError:
+@@ -639,6 +647,11 @@ class Pool(Storage):
+ except RuntimeError, err:
+ PR.warn(err)
+ PR.warn("Can not get information about btrfs pools")
++ try:
++ self._data['crypt'] = crypt.DmCryptPool(options=self.options)
++ except RuntimeError, err:
++ PR.warn(err)
++ PR.warn("Can not get information about crypt pools")
+
+ backend = self.get_backend(SSM_DEFAULT_BACKEND)
+ self.default = Item(backend, backend.default_pool_name)
+@@ -682,10 +695,18 @@ class Devices(Storage):
+ PR.warn("Can not get information about MD devices")
+ my_md = Struct()
+ my_md.data = {}
++ try:
++ my_crypt = crypt.DmCryptDevice(options=self.options)
++ except RuntimeError, err:
++ PR.warn(err)
++ PR.warn("Can not get information about crypt devices")
++ my_crypt = Struct()
++ my_crypt.data = {}
+
+ self._data['dev'] = DeviceInfo(data=dict(my_lvm.data.items() +
+ my_btrfs.data.items() +
+- my_md.data.items()),
++ my_md.data.items() +
++ my_crypt.data.items()),
+ options=self.options)
+ self.header = ['Device', 'Free', 'Used',
+ 'Total', 'Pool', 'Mount point']
+@@ -862,30 +883,41 @@ class StorageHandle(object):
+ def check(self, args):
+ """
+ Check the file system on the volume. FsInfo is used for that purpose,
+- except for btrfs.
++ except for btrfs. Or check the volume itself if backend supports it.
+ """
+ err = 0
+ checked = 0
+- for fs in args.device:
+- print "Checking {0} file system on \'{1}\':".format(fs.fstype,
+- fs.device),
+- if fs.mounted:
+- print
++ for dev in args.device:
++ if 'mount' in dev:
+ try:
+- if PR.check(PR.FS_MOUNTED, [fs.device, fs.mounted]):
+- misc.do_umount(fs.device)
++ if PR.check(PR.FS_MOUNTED, [dev['real_dev'], dev['mount']]):
++ misc.do_umount(dev['real_dev'])
+ except problem.FsMounted:
+ PR.warn("Unable to check file system " +
+- "\'{0}\' on volume \'{1}\'".format(fs.fstype,
+- fs.device))
++ "\'{0}\' on volume \'{1}\'".format(dev['fs_type'],
++ dev['real_dev']))
+ continue
+- ret = fs.fsck()
+- checked += 1
+- err += ret
+- if ret:
+- print "FAIL"
+- else:
+- print "OK"
++
++ # Does backend support check ?
++ try:
++ if getattr(dev, "check"):
++ print "Checking volume \'{0}\'.".format(dev['real_dev'])
++ ret = dev.check()
++ checked += 1
++ err += ret
++ if ret:
++ continue
++ except AttributeError:
++ pass
++
++ # Do we have a file system to check ?
++ if 'fs_info' in dev:
++ fs = dev['fs_info']
++ print "Checking {0} file system on \'{1}\'.".format(fs.fstype,
++ fs.device)
++ ret = fs.fsck()
++ checked += 1
++ err += ret
+ if checked == 0:
+ PR.error("Nothing was checked")
+ if err > 0:
+@@ -944,6 +976,14 @@ class StorageHandle(object):
+ if not self.dev[dev] or 'pool_name' not in self.dev[dev]:
+ # Check signature of existing file system on the device
+ # and ask user whether to use it or not.
++ if self.dev[dev] and 'mount' in self.dev[dev]:
++ try:
++ if PR.check(PR.FS_MOUNTED,
++ [dev, self.dev[dev]['mount']]):
++ misc.do_umount(dev)
++ except problem.FsMounted:
++ args.device.remove(dev)
++ continue
+ signature = misc.get_fs_type(dev)
+ if signature and \
+ not PR.check(PR.EXISTING_FILESYSTEM, [signature, dev]):
+@@ -977,6 +1017,10 @@ class StorageHandle(object):
+ args.pool = self.pool[args.volume['pool_name']]
+ vol_size = float(args.volume['vol_size'])
+
++ if args.pool.type == 'btrfs':
++ msg = "Resizing btrfs volume is not supported"
++ raise problem.NotSupported(msg)
++
+ if not args.size:
+ new_size = vol_size
+ elif args.size[0] == '+':
+@@ -987,7 +1031,7 @@ class StorageHandle(object):
+ new_size = float(args.size)
+ size_change = new_size - vol_size
+
+- fs = True if 'fs_type' in args.volume else False
++ fs = True if 'fs_info' in args.volume else False
+
+ if new_size <= 0:
+ PR.error("New volume size \'{0} KB\' is too small".format(new_size))
+@@ -996,28 +1040,39 @@ class StorageHandle(object):
+ # Try to grow the file system, since there is nothing to
+ # do with the volume itself.
+ if fs:
+- args.volume['fs_info'].resize()
++ ret = args.volume['fs_info'].resize()
++ if ret:
++ PR.error("File system on {0} can not be resized".format(args.volume.name))
+ else:
+ PR.check(PR.RESIZE_ALREADY_MATCH, [args.volume.name, new_size])
+ return
+
+ # Backend might not support pooling
+ if args.pool is None:
+- pool_free = None
++ pool_free = 0.0
+ pool_name = "none"
+ else:
+ pool_free = float(args.pool['pool_free'])
+ pool_name = args.pool.name
+
+- have_size, devices = self._filter_device_list(args,
+- pool_free,
+- new_size)
++ # No need to do anything with provided devices since
++ # we do have enough space to cover the resize
++ if (pool_free < size_change):
++ have_size, devices = self._filter_device_list(args,
++ pool_free,
++ new_size)
++ else:
++ have_size = pool_free
+
+ if have_size < size_change:
+ PR.check(PR.RESIZE_NOT_ENOUGH_SPACE,
+ [pool_name, args.volume.name, new_size])
+- elif len(args.device) > 0 and new_size > vol_size:
+- self.add(args, True)
++ elif len(args.device) > 0 and size_change > pool_free:
++ try:
++ self.add(args, True)
++ except problem.NotSupported:
++ # Some backends might not support pooling at all.
++ pass
+
+ if new_size != vol_size:
+ args.volume.resize(new_size, fs)
+@@ -1028,9 +1083,30 @@ class StorageHandle(object):
+ provided as arguments. If the device is not in the selected pool, then
+ add() is called on the pool prior to create().
+ """
++
++ lvname = self.create_volume(args)
++
++ if args.encrypt and misc.is_bdevice(lvname) and \
++ SSM_DEFAULT_BACKEND != 'crypt':
++ crypt = self.pool.get_backend("crypt")
++ args.pool = Item(crypt, crypt.default_pool_name)
++ options = {'encrypt': args.encrypt}
++ lvname = args.pool.create(devs=[lvname],
++ size=None,
++ options=options,
++ name=args.name)
++
++ if args.fstype and args.pool.type != 'btrfs':
++ if self._create_fs(args.fstype, lvname) != 0:
++ self._mpoint = None
++ if self._mpoint:
++ self.reinit_vol()
++ self._do_mount(self.vol[lvname])
++
++ def create_volume(self, args):
+ # Get the size in kilobytes
+- if args.size:
+- args.size = misc.get_real_size(args.size)
++# if args.size:
++# args.size = misc.get_real_size(args.size)
+
+ if self._mpoint and not (args.fstype or args.pool.type == 'btrfs'):
+ if PR.check(PR.CREATE_MOUNT_NOFS, self._mpoint):
+@@ -1049,6 +1125,12 @@ class StorageHandle(object):
+ have_size, devices = self._filter_device_list(args, pool_free,
+ args.size)
+
++ # When the pool does not exist and there is no device usable
++ # for creating the new pool, then there is no point of trying to
++ # create a volume, since it would fail in the backend anyway.
++ if not args.pool.exists() and len(devices) == 0:
++ PR.check(PR.NO_DEVICES, args.pool.name)
++
+ # Currently we do not allow setting subvolume size with btrfs. This
+ # should change in the future (quotas maybe) so the check should
+ # be removed or pushed to the backend itself.
+@@ -1058,12 +1140,6 @@ class StorageHandle(object):
+ [have_size, args.pool.name]):
+ args.size = None
+
+- # When the pool does not exist and there is no device usable
+- # for creating the new pool, then there is no point of trying to
+- # create a volume, since it would fail in the backend anyway.
+- if not args.pool.exists() and len(devices) == 0:
+- PR.check(PR.NO_DEVICES, args.pool.name)
+-
+ if have_size == 0:
+ PR.error("Not enough space ({0} KB) to".format(have_size) +
+ "to create volume")
+@@ -1080,32 +1156,52 @@ class StorageHandle(object):
+ "must not exceed number of devices " +
+ "({0})".format(tmp))
+
++ if args.raid:
++ # In raid we might has a requirement on a number of devices
++ # available as well as different requirements on the size
++ # available. We do not do any complicated math to figure out
++ # whether we really do have enough space on the devices which
++ # might differ in size. Let the respective backend tool deal
++ # with that, it's always ok to fail, but we might cover the
++ # most obvious cases here.
++ dev_count = len(args.device)
++ if args.pool.exists():
++ dev_count += int(args.pool['dev_count'])
++ if args.raid == '10' and args.stripes:
++ if args.stripes * 2 > dev_count:
++ PR.error("Not enough devices ({0}) ".format(dev_count) +
++ "for specified number of ".format(args.stripes) +
++ "stripes ({0}). You need ".format(args.stripes) +
++ "at least {0} devices".format(args.stripes * 2))
++ if args.raid == '1' and dev_count < 2:
++ PR.error("You need at least 2 devices to create " +
++ "raid1 volume")
++
+ # If we want btrfs pool and it does not exist yet, we do not
+ # want to call add since it would create it. Note that when
+ # btrfs pool is created the new btrfs volume is created as well
+ # because it is actually the same thing
+ if len(args.device) > 0 and \
+ not (not args.pool.exists() and args.pool.type == 'btrfs'):
+- self.add(args, True)
++ try:
++ self.add(args, True)
++ except problem.NotSupported:
++ # Some backends might not support pooling at all.
++ pass
+
++ options = {}
++ if args.encrypt:
++ options['encrypt'] = args.encrypt
+ if args.raid:
+- raid = {'level': args.raid,
+- 'stripesize': args.stripesize,
+- 'stripes': args.stripes}
+- else:
+- raid = None
++ options['raid'] = args.raid
++ options['stripesize'] = args.stripesize
++ options['stripes'] = args.stripes
+
+ lvname = args.pool.create(devs=devices,
+ size=args.size,
+- raid=raid,
++ options=options,
+ name=args.name)
+-
+- if args.fstype and args.pool.type != 'btrfs':
+- if self._create_fs(args.fstype, lvname) != 0:
+- self._mpoint = None
+- if self._mpoint:
+- self.reinit_vol()
+- self._do_mount(self.vol[lvname])
++ return lvname
+
+ def list(self, args):
+ """
+@@ -1150,11 +1246,20 @@ class StorageHandle(object):
+ else:
+ # Check signature of existing file system on the device
+ # and as user whether to use it or not.
+- signature = misc.get_fs_type(dev)
+- if signature and \
+- not PR.check(PR.EXISTING_FILESYSTEM, [signature, dev]):
+- args.device.remove(dev)
+- continue
++ if item and 'mount' in item:
++ try:
++ if PR.check(PR.FS_MOUNTED, [dev, item['mount']]):
++ misc.do_umount(dev)
++ except problem.FsMounted:
++ args.device.remove(dev)
++ continue
++ else:
++ signature = misc.get_fs_type(dev)
++ if signature and \
++ not PR.check(PR.EXISTING_FILESYSTEM,
++ [signature, dev]):
++ args.device.remove(dev)
++ continue
+
+ if args.pool.exists():
+ if len(args.device) > 0:
+@@ -1196,10 +1301,7 @@ class StorageHandle(object):
+ pool.reduce(item.name)
+ removed += 1
+ continue
+- else:
+- PR.error("It is not clear what do you want " +
+- "to achieve by removing " +
+- "{0}!".format(item.name))
++
+ item.remove()
+ removed += 1
+ except (RuntimeError, problem.SsmError), ex:
+@@ -1222,7 +1324,8 @@ class StorageHandle(object):
+ snap_size = vol_size * 0.20
+ user_set_size = False
+ else:
+- snap_size = float(misc.get_real_size(args.size))
++ snap_size = float(args.size)
++ #snap_size = float(misc.get_real_size(args.size))
+ user_set_size = True
+
+ if pool_free < snap_size:
+@@ -1249,17 +1352,34 @@ class StorageHandle(object):
+ "{0} with options \'{1}\'".format(args.directory,
+ args.options))
+
++ def can_check(self, device):
++ fs = self.is_fs(device)
++ if fs is False:
++ real = misc.get_real_device(device)
++ vol = self.vol[real]
++ err = "'{0}' is not valid volume to check.".format(device)
++ try:
++ if not getattr(vol, "check"):
++ raise argparse.ArgumentTypeError(err)
++ else:
++ return vol
++ except AttributeError:
++ raise argparse.ArgumentTypeError(err)
++ else:
++ return fs
++
++
+ def is_fs(self, device):
+ real = misc.get_real_device(device)
+
+ vol = self.vol[real]
+- if vol and 'fs_type' in vol:
+- return vol['fs_info']
++ if vol and 'fs_info' in vol:
++ return vol
+ dev = self.dev[real]
+- if dev and 'fs_type' in dev:
+- return dev['fs_info']
++ if dev and 'fs_info' in dev:
++ return dev
+ err = "'{0}' does not contain valid file system".format(real)
+- raise argparse.ArgumentTypeError(err)
++ return False
+
+ def _find_device_record(self, path):
+ """
+@@ -1294,7 +1414,10 @@ class StorageHandle(object):
+ return self.get_bdevice(path)
+
+ def get_bdevice(self, path):
+- path = is_bdevice(path)
++ path = misc.is_bdevice(path)
++ if path == False:
++ err = "'{0}' is not valid block device".format(path)
++ raise argparse.ArgumentTypeError(err)
+ return self._find_device_record(path)
+
+ def is_pool(self, string):
+@@ -1310,7 +1433,7 @@ class StorageHandle(object):
+ if vol:
+ return vol
+ dev = self.dev[string]
+- if dev and 'fs_type' in dev:
++ if dev and 'fs_info' in dev:
+ return dev
+ err = "'{0}' is not a valid volume to resize".format(string)
+ raise argparse.ArgumentTypeError(err)
+@@ -1366,6 +1489,21 @@ class StorageHandle(object):
+ err = "'{0}' is not valid pool nor volume".format(string)
+ raise argparse.ArgumentTypeError(err)
+
++def valid_size(size):
++ """ Validate that the 'size' is usable size argument. This is almost the
++ same as valid_resize_size() except we do not allow '+' and '-' signs
++ """
++
++ err = "'{0}' is not valid size.".format(size)
++ if len(size) and size[0] in ['+', '-']:
++ raise argparse.ArgumentTypeError(err)
++ try:
++ ret = misc.get_real_size(size)
++ if float(ret) < 0:
++ raise argparse.ArgumentTypeError(err)
++ except:
++ raise argparse.ArgumentTypeError(err)
++ return ret
+
+ def valid_resize_size(size):
+ """
+@@ -1404,7 +1542,9 @@ def valid_resize_size(size):
+ raise argparse.ArgumentTypeError(err)
+
+
+-def is_directory(self, string):
++def is_directory(string):
++ if string is None:
++ raise argparse.ArgumentTypeError(err)
+ try:
+ mode = os.stat(string).st_mode
+ except OSError:
+@@ -1417,19 +1557,6 @@ def is_directory(self, string):
+ raise argparse.ArgumentTypeError(err)
+
+
+-def is_bdevice(path):
+- path = misc.get_real_device(path)
+- try:
+- mode = os.lstat(path).st_mode
+- except OSError:
+- err = "'{0}' is not valid block device".format(path)
+- raise argparse.ArgumentTypeError(err)
+- if not stat.S_ISBLK(mode):
+- err = "'{0}' is not valid block device".format(path)
+- raise argparse.ArgumentTypeError(err)
+- return path
+-
+-
+ def is_supported_fs(fs):
+ if fs in SUPPORTED_FS:
+ return fs
+@@ -1484,6 +1611,14 @@ class SsmParser(object):
+ "({0}).".format(",".join(SUPPORTED_BACKENDS)),
+ choices=SUPPORTED_BACKENDS,
+ action=SetBackend)
++ parser.add_argument('-n', '--dry-run',
++ help='''Dry run. Do not do anything, just parse the command
++ line options and gather system information if necessary.
++ Note that with this option ssm will not perform all the
++ check as some of them are done by the backends
++ themselves. This option is mainly used for debugging
++ purposes.''',
++ action="store_true")
+ return parser
+
+ def _get_parser_check(self):
+@@ -1494,7 +1629,7 @@ class SsmParser(object):
+ help="Check consistency of the file system on the device.")
+ parser_check.add_argument('device', nargs='+',
+ help="Device with file system to check.",
+- type=self.storage.is_fs)
++ type=self.storage.can_check)
+ parser_check.set_defaults(func=self.storage.check)
+ return parser_check
+
+@@ -1535,7 +1670,8 @@ class SsmParser(object):
+ A size suffix K|k, M|m, G|g, T|t, P|p, E|e can be used
+ to define 'power of two' units. If no unit is provided, it
+ defaults to kilobytes. This is optional if if
+- not given maximum possible size will be used.''')
++ not given maximum possible size will be used.''',
++ type=valid_size)
+ parser_create.add_argument('-n', '--name',
+ help='''The name for the new logical volume. This is optional
+ and if omitted, name will be generated by the
+@@ -1571,6 +1707,10 @@ class SsmParser(object):
+ parser_create.add_argument('-p', '--pool', default="",
+ help="Pool to use to create the new volume.",
+ type=self.storage.is_pool)
++ parser_create.add_argument('-e', '--encrypt', nargs='?',
++ choices=crypt.SUPPORTED_CRYPT, const=True,
++ help='''Create encrpted volume. Extension to use can be
++ specified.''')
+ parser_create.add_argument('device', nargs='*',
+ help='''Devices to use for creating the volume. If the device
+ is not in any pool, it is added into the pool prior the
+@@ -1637,7 +1777,8 @@ class SsmParser(object):
+ A size suffix K|k, M|m, G|g, T|t, P|p, E|e can be used
+ to define 'power of two' units. If no unit is provided, it
+ defaults to kilobytes. This is option and if not give,
+- the size will be determined automatically.''')
++ the size will be determined automatically.''',
++ type=valid_size)
+ group = parser_snapshot.add_mutually_exclusive_group()
+ group.add_argument('-d', '--dest',
+ help='''Destination of the snapshot specified with absolute
+@@ -1705,6 +1846,9 @@ def main(args=None):
+ # Register clean-up function on exit
+ sys.exitfunc = misc.do_cleanup
+
++ if args.dry_run:
++ return 0;
++
+ try:
+ args.func(args)
+ except argparse.ArgumentTypeError, ex:
+diff --git a/ssmlib/misc.py b/ssmlib/misc.py
+index 8e731ff..3ff1577 100644
+--- a/ssmlib/misc.py
++++ b/ssmlib/misc.py
+@@ -20,6 +20,7 @@
+ import os
+ import re
+ import sys
++import stat
+ import tempfile
+ import threading
+ import subprocess
+@@ -191,13 +192,15 @@ def do_mount(device, directory, options=None):
+ run(command)
+
+
+-def do_umount(mpoint):
+- command = ['umount', mpoint]
++def do_umount(mpoint, all_targets=False):
++ command = ['umount']
++ if all_targets:
++ command.append('--all-targets')
+ try:
+- run(command)
++ run(command + [mpoint])
+ except RuntimeError:
+- command = ['umount', '-l', mpoint]
+- run(command)
++ command.append('-l')
++ run(command + mpoint)
+
+
+ def temp_mount(device, options=None):
+@@ -228,12 +231,13 @@ def get_signature(device, types=None):
+ command.extend(['-u', types])
+ command.append(device)
+
+- output = run(command, can_fail=True)[1].strip()
++ ret, output = run(command, can_fail=True, stderr=False)
++ output = output.strip()
+
+- if len(output) > 0:
+- return output
+- else:
++ if ret:
+ return None
++ else:
++ return output
+
+
+ def get_fs_type(device):
+@@ -319,16 +323,21 @@ def get_dmnumber(name):
+ return dmnumber
+
+
+-def wipefs(device, typ):
++def wipefs(device, signatures):
++ if type(signatures) is not list:
++ signatures = [signatures]
+ command = ['wipefs', '-p', device]
+ output = run(command)[1]
++ offset = []
+ for line in output[1:].split('\n'):
+ if not line:
+ continue
+ array = line.split(",")
+- if array[-1] == typ:
+- command = ['wipefs', '--offset', array[0], device]
+- run(command)
++ if array[-1] in signatures:
++ offset.extend(['--offset', array[0]])
++ if len(offset) > 1:
++ command = ['wipefs'] + offset + [device]
++ run(command)
+
+
+ def humanize_size(arg):
+@@ -545,3 +554,26 @@ def terminal_size(default=(25, 80)):
+ if not cr:
+ cr = (25, 80)
+ return int(cr[1]), int(cr[0])
++
++
++def is_bdevice(path):
++ """
++ Check whether the path is block device. If it is return
++ the path to the real block device, otherwise return False
++ """
++ path = get_real_device(path)
++ try:
++ mode = os.lstat(path).st_mode
++ except OSError:
++ return False
++ if not stat.S_ISBLK(mode):
++ return False
++ return path
++
++
++def get_device_size(device):
++ devname = device.split('/')[-1]
++ with open("/sys/block/{0}/size".format(devname), 'r') as f:
++ for line in f:
++ size = int(line)/2
++ return size
+diff --git a/ssmlib/problem.py b/ssmlib/problem.py
+index 56846df..dfa6ec6 100644
+--- a/ssmlib/problem.py
++++ b/ssmlib/problem.py
+@@ -22,7 +22,8 @@ import sys
+ __all__ = ["ProblemSet", "SsmError", "GeneralError", "ProgrammingError",
+ "BadEnvVariable", "NotEnoughSpace", "ResizeMatch", "FsNotSpecified",
+ "DeviceUsed", "ExistingFilesystem", "NoDevices", "ToolMissing",
+- "CanNotRun", "CommandFailed", "UserInterrupted", "NotSupported"]
++ "CanNotRun", "CommandFailed", "UserInterrupted", "NotSupported",
++ "NotImplemented"]
+
+ # Define prompt codes
+ PROMPT_NONE = 0
+@@ -52,9 +53,10 @@ FL_DEFAULT_NO = 16
+ FL_SILENT = 32
+ FL_EXIT_ON_NO = 64
+ FL_EXIT_ON_YES = 128
+-FL_FATAL = 256
++FL_NO_MESSAGE = 256
+ FL_FORCE_YES = 512
+ FL_FORCE_NO = 1024
++FL_FATAL = (2048 | FL_NO_MESSAGE)
+
+
+ class SsmError(Exception):
+@@ -65,7 +67,7 @@ class SsmError(Exception):
+ self.errcode = errcode
+
+ def __str__(self):
+- return repr("Error ({0}): {1}".format(self.errcode, self.msg))
++ return "SSM Error ({0}): {1}".format(self.errcode, self.msg)
+
+
+ class GeneralError(SsmError):
+@@ -142,6 +144,10 @@ class ExistingFilesystem(SsmError):
+ def __init__(self, msg, errcode=2015):
+ super(ExistingFilesystem, self).__init__(msg, errcode)
+
++class NotImplemented(SsmError):
++ def __init__(self, msg, errcode=2016):
++ super(NotImplemented, self).__init__(msg, errcode)
++
+
+ class ProblemSet(object):
+
+@@ -158,7 +164,7 @@ class ProblemSet(object):
+ PROMPT_NONE, FL_FATAL, ProgrammingError]
+
+ self.GENERAL_ERROR = \
+- ['SSM Error: {0}!', PROMPT_NONE, FL_FATAL, GeneralError]
++ ['{0}!', PROMPT_NONE, FL_FATAL, GeneralError]
+
+ self.GENERAL_INFO = \
+ ['SSM Info: {0}', PROMPT_NONE, FL_NONE, None]
+@@ -222,11 +228,17 @@ class ProblemSet(object):
+ ['{0} is not supported!',
+ PROMPT_NONE, FL_FATAL, NotSupported]
+
++ self.NOT_IMPLEMENTED = \
++ ['\'{0}\' function is not implemented by {1}!',
++ PROMPT_NONE, FL_FATAL, NotImplemented]
++
+ def _can_print_message(self, flags):
+ if (flags & FL_DEBUG_ONLY):
+ return self.options.debug
+ elif (flags & FL_VERBOSE_ONLY):
+ return self.options.verbose
++ elif (flags & FL_NO_MESSAGE):
++ return False
+ else:
+ return True
+
+@@ -289,14 +301,10 @@ class ProblemSet(object):
+
+ if self._can_print_message(flags) and \
+ (flags & FL_MSG_ONLY or prompt_msg is None):
+- print >> sys.stderr, message,
+- else:
+- print message,
++ print >> sys.stderr, message
+ if not flags & FL_MSG_ONLY and prompt_msg is not None:
+- print '{0}'.format(prompt_msg),
++ print message, '{0}'.format(prompt_msg),
+ res = self._ask_question(flags)
+- else:
+- print >> sys.stderr
+
+ if (flags & FL_FATAL):
+ if exc:
+diff --git a/tests/bashtests/001-lvm-add.sh b/tests/bashtests/001-lvm-add.sh
+index fee51a8..cfe95da 100755
+--- a/tests/bashtests/001-lvm-add.sh
++++ b/tests/bashtests/001-lvm-add.sh
+@@ -131,6 +131,16 @@ mkfs.ext3 $dev1
+ # Default answer is No
+ not ssm add $dev1
+ not check vg_field $SSM_LVM_DEFAULT_POOL pv_count 1
++# It can be forced though
++ssm -f add $dev1
++check vg_field $SSM_LVM_DEFAULT_POOL pv_count 1
++ssm -f remove $SSM_LVM_DEFAULT_POOL
++
++# Try to use device with existing file system
++mkfs.ext3 $dev1
++# Default answer is No
++not ssm add $dev1
++not check vg_field $SSM_LVM_DEFAULT_POOL pv_count 1
+ ssm add $dev1 $dev2
+ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 1
+ ssm -f remove --all
+diff --git a/tests/bashtests/002-lvm-create.sh b/tests/bashtests/002-lvm-create.sh
+index aceea6d..d316e5d 100755
+--- a/tests/bashtests/002-lvm-create.sh
++++ b/tests/bashtests/002-lvm-create.sh
+@@ -115,6 +115,64 @@ check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 10 none none 960.0
+ check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL 960.00MB striped
+ ssm -f remove $SSM_LVM_DEFAULT_POOL
+
++# Create a raid 1 logical volume
++# Size of the volume must be specified for lvm raid1
++not ssm create -r 1 $dev1 $dev2
++size=$((DEV_SIZE/2))
++size=$(align_size_up $size)
++ssm create -s ${size}M -r 1 $dev1 $dev2
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 segtype raid1
++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 2 none none 192.00MB
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid1
++
++ssm add $TEST_DEVS
++size=$((DEV_SIZE*2))
++size=$(align_size_up $size)
++ssm create -s ${size}M -r 1
++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 10 none none 960.00MB
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol2 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid1
++ssm -f remove $SSM_LVM_DEFAULT_POOL
++
++# Create a raid 10 logical volume
++# Size of the volume must be specified for lvm raid10
++not ssm create -r 10 $dev1 $dev2 $dev3 $dev4
++# Minimum is 4 devices
++not ssm create -s ${DEV_SIZE}M -r 10 $dev1 $dev2
++# Number of device has to be even
++not ssm create -s ${DEV_SIZE}M -r 10 $dev1 $dev2 $dev3 $dev4 $dev5
++ssm remove $dev5
++ssm create -s ${DEV_SIZE}M -r 10 $dev1 $dev2 $dev3 $dev4
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 stripes 4
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 segtype raid10
++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 4 none none 384.00MB
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL 104.00MB raid1
++size=56
++# Stripes or device has to be specified
++not ssm create -s ${size}M -r 10
++ssm create -s ${size}M -r 10 --stripes 2
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol2 stripes 4
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol2 segtype raid10
++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 4 none none 384.00MB
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol2 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid10
++ssm add $TEST_DEVS
++size=60
++ssm create -s ${size}M -r 10 --stripes 3
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol3 stripes 6
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol3 segtype raid10
++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 10 none none 960.00MB
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol3 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid10
++ssm -f remove $SSM_LVM_DEFAULT_POOL
++ssm add $TEST_DEVS
++size=$((DEV_SIZE*2))
++size=$(align_size_up $size)
++ssm create -s ${size}M -r 10 $TEST_DEVS
++ssm list
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 stripes 10
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 segtype raid10
++check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL lvm 10 none none 960.00MB
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL ${size}.00MB raid10
++ssm -f remove $SSM_LVM_DEFAULT_POOL
++
+ # Create several volumes with different parameters
+ ssm add $TEST_DEVS
+ not ssm create -I 8 -i $(($DEV_COUNT/2)) -s $(($DEV_SIZE*2))M
+@@ -174,7 +232,8 @@ for fs in $TEST_FS; do
+ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
+
+ # pool doesn't exist
+- ssm create --fstype $fs -s $((DEV_SIZE/2))m -n randvol $dev4 $mnt2
++ ssm create --fstype $fs -s $((DEV_SIZE/2))m -n randvol $dev4
++ ssm mount $SSM_LVM_DEFAULT_POOL/randvol $mnt2
+ size=`align_size_up $((DEV_SIZE/2))`
+ check lv_field $SSM_LVM_DEFAULT_POOL/randvol lv_size $size.00m
+ check mountpoint $SSM_LVM_DEFAULT_POOL-randvol $mnt2
+@@ -282,4 +341,8 @@ not ssm create
+ ssm add $TEST_DEVS
+ not ssm create -p $pool1
+ not ssm create -r 0 -I 16 -i 3 $dev1 $dev2
++not ssm create -s-20M
++not ssm create -s+20M
++not ssm create --size 25.8.1991
++not ssm create --size linux
+ ssm -f remove --all
+diff --git a/tests/bashtests/004-lvm-resize.sh b/tests/bashtests/004-lvm-resize.sh
+index f430851..c863f36 100755
+--- a/tests/bashtests/004-lvm-resize.sh
++++ b/tests/bashtests/004-lvm-resize.sh
+@@ -48,20 +48,79 @@ TEST_MNT=$TESTDIR/mnt
+
+ _test_resize()
+ {
+- size=$((TEST_MAX_SIZE/2))
+- echo 'y' | ssm -f resize --size ${size}M ${DM_DEV_DIR}/$DEFAULT_VOLUME
++ # Test with no device
++ # Test size increase
++ size=$DEV_SIZE
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ size=$((DEV_SIZE + 12))
++ ssm resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ size=$((size + 3))
++ ssm resize -s +3M $SSM_LVM_DEFAULT_POOL/$lvol1
+ size=$(align_size_up $size)
+ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ # Test size decrese
++ if [ "$fs" != "xfs" ]; then
++ size=$((size - 8))
++ ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ size=$((size - 7))
++ ssm -f resize -s-7M $SSM_LVM_DEFAULT_POOL/$lvol1
++ size=$(align_size_up $size)
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ # Test with devcie belongs to no pool
++ # size decrease
++ size=$((size - 12))
++ ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev4
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
++ check vg_field $pool1 pv_count 3
++ fi
++ # If the volume is already of the given size ssm will attempt to resize
++ # file system to cover the whole device. Note that we do not check for
++ # the file system size because it's not really necessary. So this would
++ # fail if the file system is present. This might change in future, so
++ # comment it out for now.
++ # size doesn't change
++ #not ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev4
++ #check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ #check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
++ #check vg_field $pool1 pv_count 3
++ # size increase
++ size=$((size + 12))
++ ssm resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev4
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
++ check vg_field $pool1 pv_count 3
+
+- # xfs does not support shrinking (xfs only grows big!! :))
++ # Test with device belongs to other pool
++ # size decrease
+ if [ "$fs" != "xfs" ]; then
+- ssm -f -v resize -s-$(($TEST_MAX_SIZE/4))M $DEFAULT_VOLUME
+- size=$(align_size_up $(($size-($TEST_MAX_SIZE/4))))
++ size=$((size - 12))
++ ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev6
+ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
++ check vg_field $pool1 pv_count 3
+ fi
+- echo 'y' | ssm -f resize --size +$(($TEST_MAX_SIZE/5))M $DEFAULT_VOLUME
+- size=$(align_size_up $(($size+($TEST_MAX_SIZE/5))))
++ # size doesn't change
++ #not ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev6
++ #check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ #check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
++ #check vg_field $pool1 pv_count 3
++ # size increase
++ size=$((size + 12))
++ ssm -f resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev6
++ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 3
++ check vg_field $pool1 pv_count 3
++
++ # when resize to excessive amount
++ size=$((DEV_SIZE*4))
++ not ssm resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1
++ ssm resize -s ${size}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev8 $dev9
+ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 lv_size ${size}.00m
++ check vg_field $SSM_LVM_DEFAULT_POOL pv_count 5
++
+ }
+
+ ssm add $TEST_DEVS
+@@ -100,46 +159,37 @@ ssm -f remove $SSM_LVM_DEFAULT_POOL
+ ssm create --size $((DEV_SIZE/2))M $dev1
+ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 1
+ ssm resize --size +$((DEV_SIZE/3))M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2
+-check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 2
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 1
+ ssm -f resize -s-$((DEV_SIZE/3))M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2 $dev3
+ ssm resize --size +$((DEV_SIZE/3))M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2 $dev3
+-check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 3
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 1
+ ssm -f resize -s-$((DEV_SIZE/3))M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2 $dev3
+-check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 3
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 1
+ ssm resize --size +${DEV_SIZE}M $SSM_LVM_DEFAULT_POOL/$lvol1 $dev2 $dev3
+ check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 3
+ ssm -f remove $SSM_LVM_DEFAULT_POOL
+
++
++ssm add $dev{1,2,3}
++ssm create -s ${DEV_SIZE}M
++ssm add -p $pool1 $dev{5,6,7}
++_test_resize
++ssm -f remove $SSM_LVM_DEFAULT_POOL
++ssm -f remove $pool1
++
++
+ [ ! -d $TEST_MNT ] && mkdir $TEST_MNT &> /dev/null
+ for fs in $TEST_FS; do
+- # umounted test
+- ssm add $TEST_DEVS
+- size=$((TEST_MAX_SIZE/4))
+- ssm create --fs $fs --size ${size}M $TEST_DEVS
+-
++ ssm add $dev{1,2,3}
++ ssm create -s ${DEV_SIZE}M --fs $fs
++ ssm -f check $DEFAULT_VOLUME
++ ssm add -p $pool1 $dev{5,6,7}
+ _test_resize
+ ssm -f check $DEFAULT_VOLUME
+ ssm -f remove $SSM_LVM_DEFAULT_POOL
+-
+- echo $fs
+- if [ $fs == 'ext2' ]; then
+- continue
+- fi
+-
+- # Disable this for now, since fsadm does not handle -f and -y correctly
+- # mounted test
+- #ssm add $TEST_DEVS
+- size=$((TEST_MAX_SIZE/4))
+- #ssm create --fs $fs --size ${size}M $TEST_DEVS
+-
+- #mount ${DM_DEV_DIR}/$DEFAULT_VOLUME $TEST_MNT
+-
+- #_test_resize
+-
+- #umount $TEST_MNT
+- #ssm -f check $DEFAULT_VOLUME
+- #ssm -f remove $SSM_LVM_DEFAULT_POOL
++ ssm -f remove $pool1
+ done
++# There should not be anything to remove
+ not ssm -f remove --all
+
+ ssm create $dev1
+diff --git a/tests/bashtests/007-btrfs-create.sh b/tests/bashtests/007-btrfs-create.sh
+index 1ea27df..c971a76 100755
+--- a/tests/bashtests/007-btrfs-create.sh
++++ b/tests/bashtests/007-btrfs-create.sh
+@@ -88,6 +88,7 @@ ssm -f remove $SSM_BTRFS_DEFAULT_POOL
+
+ # Create raid 10 volume with just one device
+ ssm create -r 10 $dev1 $dev2 $dev3 $dev4
++ssm check $SSM_BTRFS_DEFAULT_POOL
+ not ssm create $dev1 -p $pool1
+ ssm -f remove $SSM_BTRFS_DEFAULT_POOL
+
+diff --git a/tests/bashtests/011-lvm-list.sh b/tests/bashtests/011-lvm-list.sh
+index 66567a1..73cea90 100755
+--- a/tests/bashtests/011-lvm-list.sh
++++ b/tests/bashtests/011-lvm-list.sh
+@@ -1,6 +1,6 @@
+ #!/bin/bash
+ #
+-# (C)2012 Red Hat, Inc., Tom Marek <tmarek at redhat.com>
++# (C)2013 Red Hat, Inc., Jimmy Pan <jipan at redhat.com>
+ #
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+@@ -21,9 +21,10 @@ export test_description='Check whether list command prints correct values for lv
+ . lib/test
+
+ DEV_COUNT=10
+-DEV_SIZE=100
++DEV_SIZE=128
+ TEST_MAX_SIZE=$(($DEV_COUNT*$DEV_SIZE))
+ aux prepare_devs $DEV_COUNT $DEV_SIZE
++aux prepare_mnts 10
+ TEST_DEVS=$(cat DEVICES)
+ export LVOL_PREFIX="lvol"
+ export SSM_DEFAULT_BACKEND='lvm'
+@@ -33,8 +34,14 @@ export SSM_NONINTERACTIVE='1'
+ snap1="snap1"
+ snap2="snap2"
+
++lvol1=${LVOL_PREFIX}001
++lvol2=${LVOL_PREFIX}002
++lvol3=${LVOL_PREFIX}003
++
++pool0=$vg1
+ pool1=$vg2
+ pool2=$vg3
++pool3=$vg4
+
+ TEST_FS=
+ which mkfs.ext2 && TEST_FS+="ext2 "
+@@ -45,28 +52,262 @@ which mkfs.xfs && TEST_FS+="xfs"
+ TEST_MNT=$TESTDIR/mnt
+ [ ! -d $TEST_MNT ] && mkdir $TEST_MNT &> /dev/null
+
+-##LVM
+-# Check devices
+-ssm add $TEST_DEVS
+-ssm_output=$(ssm list dev)
+-for device in ${TEST_DEVS}; do
+- check list_table "$ssm_output" $device 96.00MB 0.00KB 96.00MB $SSM_LVM_DEFAULT_POOL
++# Prepare pools and volumes
++
++vol1=volsf
++vol2=volss
++vol3=volmf
++vol4=volms1
++vol5=volms2
++vol6=volms3
++maxvolsz=$((DEV_SIZE-4))
++size1=$maxvolsz
++size2=$((DEV_SIZE/2))
++size3=$((maxvolsz*2))
++size4=$((DEV_SIZE/2))
++size5=$((DEV_SIZE*2))
++size6=$((DEV_SIZE/4))
++size4s=$((size4-20))
++size2r=$((size2-4))
++size5r=$((size5+16))
++
++
++# Test without a filesystem
++ssm -f create -n $vol1 $dev1
++ssm create -n $vol2 -p $pool1 -s ${size2}M $dev2
++ssm create -n $vol3 -p $pool2 $dev3 $dev4
++ssm add -p $pool3 $dev{5,6,7,8}
++ssm create -p $pool3 -s ${size4}m -n $vol4
++ssm create -p $pool3 -s ${size5}m -n $vol5
++ssm create -p $pool3 -s ${size6}m -n $vol6
++
++# We shouldn't see ssm list fs here
++test `ssm list fs | wc -l` -le 1
++# Check vol, dev, pool, resized vol, and snapshot
++output=`ssm list vol`
++check list_table "$output" $pool0/$vol1 $pool0 $((size1)).00MB linear
++check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB linear
++check list_table "$output" $pool2/$vol3 $pool2 $((size3)).00MB linear
++check list_table "$output" $pool3/$vol4 $pool3 $((size4)).00MB linear
++check list_table "$output" $pool3/$vol5 $pool3 $((size5)).00MB linear
++check list_table "$output" $pool3/$vol6 $pool3 $((size6)).00MB linear
++output=`ssm list dev`
++check list_table "$output" $dev1 0.00KB $((size1)).00MB 124.00MB $pool0
++check list_table "$output" $dev2 $((maxvolsz-size2)).00MB $((size2)).00MB 124.00MB $pool1
++check list_table "$output" $dev3 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
++check list_table "$output" $dev4 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
++check list_table "$output" $dev5 none none 124.00MB $pool3
++check list_table "$output" $dev6 none none 124.00MB $pool3
++check list_table "$output" $dev7 none none 124.00MB $pool3
++check list_table "$output" $dev8 none none 124.00MB $pool3
++output=`ssm list pool`
++check list_table "$output" $pool0 lvm 1 0.00KB $((size1)).00MB $((maxvolsz)).00MB
++check list_table "$output" $pool1 lvm 1 $((maxvolsz-size2)).00MB $((size2)).00MB $((maxvolsz)).00MB
++check list_table "$output" $pool2 lvm 2 0.00KB $((size3)).00MB $((maxvolsz*2)).00MB
++check list_table "$output" $pool3 lvm 4 $((maxvolsz*4-size4-size5-size6)).00MB $((size4+size5+size6)).00MB $((maxvolsz*4)).00MB
++
++# Check ssm vol after resize
++ssm -f resize $pool1/$vol2 -s ${size2r}M
++ssm resize -s ${size5r}m $pool3/$vol5
++output=`ssm list vol`
++check list_table "$output" $pool1/$vol2 $pool1 $size2r.00MB linear
++check list_table "$output" $pool3/$vol5 $pool3 $((size5r)).00MB linear
++
++ssm snapshot $pool3/$vol4 -n snap1
++ssm snapshot $pool3/$vol4 -s ${size4s}m -n snap2
++output=`ssm list snap`
++check list_table "$output" $pool3/snap1 $vol4 $pool3 none none linear
++check list_table "$output" $pool3/snap2 $vol4 $pool3 $size4s.00MB none linear
++
++
++ssm -f remove -a
++
++
++# Test with filesystem
++for fs in $TEST_FS ; do
++ ssm -f create -n $vol1 $dev1 --fs $fs
++ ssm create -n $vol2 -p $pool1 -s ${size2}M $dev2 --fs $fs
++ ssm create -n $vol3 -p $pool2 $dev3 $dev4 --fs $fs
++ ssm add -p $pool3 $dev{5,6,7,8}
++ ssm create -p $pool3 -s ${size4}m -n $vol4 --fs $fs
++ ssm create -p $pool3 -s ${size5}m -n $vol5 --fs $fs
++ ssm create -p $pool3 -s ${size6}m -n $vol6 --fs $fs
++
++ # Check fs, vol, dev, pool, resized vol, and snapshot
++ output=`ssm list fs`
++ # xfs has some strange used size, so we don't check it
++ if [ $fs == xfs ] ; then
++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs none none linear
++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs none none linear
++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs none none linear
++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs none none linear
++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs none none linear
++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs none none linear
++ else
++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs $size1.00MB none linear
++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs $size2.00MB none linear
++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs $size3.00MB none linear
++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs $size4.00MB none linear
++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs $size5.00MB none linear
++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs $size6.00MB none linear
++ fi
++
++ output=`ssm list vol`
++ # xfs has some strange used size, so we don't check it
++ if [ $fs == xfs ] ; then
++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs none none linear
++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs none none linear
++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs none none linear
++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs none none linear
++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs none none linear
++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs none none linear
++ else
++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs $size1.00MB none linear
++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs $size2.00MB none linear
++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs $size3.00MB none linear
++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs $size4.00MB none linear
++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs $size5.00MB none linear
++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs $size6.00MB none linear
++ fi
++
++ output=`ssm list dev`
++ check list_table "$output" $dev1 0.00KB $((size1)).00MB 124.00MB $pool0
++ check list_table "$output" $dev2 $((maxvolsz-size2)).00MB $((size2)).00MB 124.00MB $pool1
++ check list_table "$output" $dev3 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
++ check list_table "$output" $dev4 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
++ check list_table "$output" $dev5 none none 124.00MB $pool3
++ check list_table "$output" $dev6 none none 124.00MB $pool3
++ check list_table "$output" $dev7 none none 124.00MB $pool3
++ check list_table "$output" $dev8 none none 124.00MB $pool3
++ output=`ssm list pool`
++ check list_table "$output" $pool0 lvm 1 0.00KB $((size1)).00MB $((maxvolsz)).00MB
++ check list_table "$output" $pool1 lvm 1 $((maxvolsz-size2)).00MB $((size2)).00MB $((maxvolsz)).00MB
++ check list_table "$output" $pool2 lvm 2 0.00KB $((size3)).00MB $((maxvolsz*2)).00MB
++ check list_table "$output" $pool3 lvm 4 $((maxvolsz*4-size4-size5-size6)).00MB $((size4+size5+size6)).00MB $((maxvolsz*4)).00MB
++
++ # Check ssm vol after resize
++ # xfs size cannot reduce
++ if [ "$fs" != xfs ] ; then
++ ssm -f resize $pool1/$vol2 -s ${size2r}M
++ fi
++ ssm resize -s ${size5r}m $pool3/$vol5
++
++ output=`ssm list vol`
++ # xfs has some strange used size, so we don't check it
++ if [ "$fs" != xfs ] ; then
++ check list_table "$output" $pool1/$vol2 $pool1 $size2r.00MB $fs $size2r none linear
++ check list_table "$output" $pool3/$vol5 $pool3 $size5r.00MB $fs $size5r none linear
++ else
++ check list_table "$output" $pool3/$vol5 $pool3 $size5r.00MB $fs none none linear
++ fi
++
++ ssm snapshot $pool3/$vol4 -n snap1
++ ssm snapshot $pool3/$vol4 -s ${size4s}m -n snap2
++
++ output=`ssm list snap`
++ check list_table "$output" $pool3/snap1 $vol4 $pool3 none none linear
++ check list_table "$output" $pool3/snap2 $vol4 $pool3 $size4s.00MB none linear
++
++ ssm -f remove -a
++
+ done
+
+-# Check pools
+-check list_table "$(ssm list pool)" $SSM_LVM_DEFAULT_POOL $SSM_DEFAULT_BACKEND $DEV_COUNT none none 960.00MB
+-ssm -f remove --all
++# Test with a mountpoint
++for fs in $TEST_FS ; do
++ ssm -f create -n $vol1 --fs $fs $dev1 $mnt1
++ ssm create -n $vol2 -p $pool1 -s ${size2}M --fs $fs $dev2 $mnt2
++ ssm create -n $vol3 -p $pool2 --fs $fs $dev3 $dev4 $mnt3
++ ssm add -p $pool3 $dev{5,6,7,8}
++ ssm create -p $pool3 -s ${size4}m -n $vol4 --fs $fs $mnt4
++ ssm create -p $pool3 -s ${size5}m -n $vol5 --fs $fs $mnt5
++ ssm create -p $pool3 -s ${size6}m -n $vol6 --fs $fs $mnt6
+
+-# create multiple pools
+-ssm create --pool $pool1 $dev1 $dev2 $dev3 $dev4
+-ssm create --pool $pool2 $dev5 $dev6 $dev7 $dev8
+-ssm_output=$(ssm list pool)
+-check list_table "$ssm_output" $pool1 lvm 4 none none 384.00MB
+-check list_table "$ssm_output" $pool2 lvm 4 none none 384.00MB
+-ssm -f remove --all
++ # Check fs, vol, dev, pool, resized vol, and snapshot
++ output=`ssm list fs`
++ # xfs has some strange used size, so we don't check it
++ if [ $fs == xfs ] ; then
++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs none none linear $mnt1
++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs none none linear $mnt2
++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs none none linear $mnt3
++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs none none linear $mnt4
++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs none none linear $mnt5
++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs none none linear $mnt6
++ else
++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs $size1.00MB none linear $mnt1
++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs $size2.00MB none linear $mnt2
++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs $size3.00MB none linear $mnt3
++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs $size4.00MB none linear $mnt4
++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs $size5.00MB none linear $mnt5
++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs $size6.00MB none linear $mnt6
++ fi
+
+-ssm add $TEST_DEVS
++ output=`ssm list vol`
++ # xfs has some strange used size, so we don't check it
++ if [ $fs == xfs ] ; then
++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs none none linear $mnt1
++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs none none linear $mnt2
++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs none none linear $mnt3
++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs none none linear $mnt4
++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs none none linear $mnt5
++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs none none linear $mnt6
++ else
++ check list_table "$output" $pool0/$vol1 $pool0 $size1.00MB $fs $size1.00MB none linear $mnt1
++ check list_table "$output" $pool1/$vol2 $pool1 $size2.00MB $fs $size2.00MB none linear $mnt2
++ check list_table "$output" $pool2/$vol3 $pool2 $size3.00MB $fs $size3.00MB none linear $mnt3
++ check list_table "$output" $pool3/$vol4 $pool3 $size4.00MB $fs $size4.00MB none linear $mnt4
++ check list_table "$output" $pool3/$vol5 $pool3 $size5.00MB $fs $size5.00MB none linear $mnt5
++ check list_table "$output" $pool3/$vol6 $pool3 $size6.00MB $fs $size6.00MB none linear $mnt6
++ fi
++
++ output=`ssm list dev`
++ check list_table "$output" $dev1 0.00KB $((size1)).00MB 124.00MB $pool0
++ check list_table "$output" $dev2 $((maxvolsz-size2)).00MB $((size2)).00MB 124.00MB $pool1
++ check list_table "$output" $dev3 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
++ check list_table "$output" $dev4 0.00KB $((maxvolsz)).00MB 124.00MB $pool2
++ check list_table "$output" $dev5 none none 124.00MB $pool3
++ check list_table "$output" $dev6 none none 124.00MB $pool3
++ check list_table "$output" $dev7 none none 124.00MB $pool3
++ check list_table "$output" $dev8 none none 124.00MB $pool3
++ output=`ssm list pool`
++ check list_table "$output" $pool0 lvm 1 0.00KB $((size1)).00MB $((maxvolsz)).00MB
++ check list_table "$output" $pool1 lvm 1 $((maxvolsz-size2)).00MB $((size2)).00MB $((maxvolsz)).00MB
++ check list_table "$output" $pool2 lvm 2 0.00KB $((size3)).00MB $((maxvolsz*2)).00MB
++ check list_table "$output" $pool3 lvm 4 $((maxvolsz*4-size4-size5-size6)).00MB $((size4+size5+size6)).00MB $((maxvolsz*4)).00MB
++
++ # Check ssm vol after resize
++ # xfs size cannot reduce
++ if [ "$fs" != xfs ] ; then
++ umount $mnt2
++ ssm -f resize $pool1/$vol2 -s ${size2r}M
++ fi
++ ssm resize -s ${size5r}m $pool3/$vol5
++
++ output=`ssm list vol`
++ # xfs has some strange used size, so we don't check it
++ if [ "$fs" != xfs ] ; then
++ check list_table "$output" $pool1/$vol2 $pool1 $size2r.00MB $fs $size2r none linear
++ check list_table "$output" $pool3/$vol5 $pool3 $size5r.00MB $fs $size5r none linear
++ else
++ check list_table "$output" $pool3/$vol5 $pool3 $size5r.00MB $fs none none linear
++ fi
+
++ ssm snapshot $pool3/$vol4 -n snap1
++ ssm snapshot $pool3/$vol4 -s ${size4s}m -n snap2
++
++ output=`ssm list snap`
++ check list_table "$output" $pool3/snap1 $vol4 $pool3 none none linear
++ check list_table "$output" $pool3/snap2 $vol4 $pool3 $size4s.00MB none linear
++
++ for i in {1..6} ; do
++ mntdir=`eval echo '$mnt'$i`
++ umount $mntdir || true
++ done
++
++ ssm -f remove -a
++
++done
++
++ssm add $TEST_DEVS
+ # Check LVM volume listings with various fs
+ for fs in $TEST_FS; do
+ name="${fs}vol"
+@@ -85,30 +326,5 @@ for fs in $TEST_FS; do
+ done
+ ssm -f remove $SSM_LVM_DEFAULT_POOL
+
+-# Check lvm snapshot
+-lvol1=${LVOL_PREFIX}001
+-# Create volume with all devices at once
+-size=$(($DEV_SIZE*6))
+-snap_size1=$(($DEV_SIZE))
+-snap_size2=$(($size/5))
+-ssm create --size ${size}M $TEST_DEVS
+-ssm snapshot --name $snap1 --size ${snap_size1}M $SSM_LVM_DEFAULT_POOL/$lvol1
+-ssm snapshot --name $snap2 --size ${snap_size2}M $SSM_LVM_DEFAULT_POOL/$lvol1
+-ssm_output=$(ssm list snap)
+-check list_table "$ssm_output" $snap1 $lvol1 $SSM_LVM_DEFAULT_POOL ${snap_size1}.00MB 0.00KB linear
+-check list_table "$ssm_output" $snap2 $lvol1 $SSM_LVM_DEFAULT_POOL ${snap_size2}.00MB 0.00KB linear
+-ssm -f remove --all
+-
+-# Snapshot of the volumes in defferent pools
+-ssm create --pool $pool1 $dev1 $dev2
+-ssm add $dev3 $dev4 --pool $pool1
+-ssm create --pool $pool2 $dev5 $dev6
+-ssm add $dev7 $dev8 --pool $pool2
+-ssm snapshot --name $snap1 $pool1/$lvol1
+-ssm snapshot --name $snap1 $pool2/$lvol1
+-ssm_output=$(ssm list snap)
+-check list_table "$ssm_output" "$pool1/$snap1" $lvol1 $pool1 40.00MB 0.00KB linear
+-check list_table "$ssm_output" "$pool2/$snap1" $lvol1 $pool2 40.00MB 0.00KB linear
+-ssm -f remove --all
+-
+-# all_done!!!
++# Some situation should fail
++not ssm list wrong_type
+diff --git a/tests/bashtests/012-crypt-create.sh b/tests/bashtests/012-crypt-create.sh
+new file mode 100644
+index 0000000..8a5fddb
+--- /dev/null
++++ b/tests/bashtests/012-crypt-create.sh
+@@ -0,0 +1,143 @@
++#!/bin/bash
++#
++# (C)2013 Red Hat, Inc., Lukas Czerner <lczerner at redhat.com>
++#
++# This program is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++export test_name='012-crypt-create'
++test_description='Exercise crypt create'
++
++. lib/test
++
++DEV_COUNT=10
++DEV_SIZE=100
++TEST_MAX_SIZE=$(($DEV_COUNT*$DEV_SIZE))
++aux prepare_devs $DEV_COUNT $DEV_SIZE
++aux prepare_mnts 4
++TEST_DEVS=$(cat DEVICES)
++export SSM_DEFAULT_BACKEND='crypt'
++export SSM_CRYPT_DEFAULT_POOL=$vg1
++export CRYPT_VOL_PREFIX="${SSM_PREFIX_FILTER}enc"
++export SSM_NONINTERACTIVE='1'
++export SSM_CRYPT_DEFAULT_VOL_PREFIX=$CRYPT_VOL_PREFIX
++crypt_vol1=${CRYPT_VOL_PREFIX}001
++crypt_vol2=${CRYPT_VOL_PREFIX}002
++crypt_vol3=${CRYPT_VOL_PREFIX}003
++crypt_vol4=${CRYPT_VOL_PREFIX}004
++passwd="cai0ohMo8M"
++
++pool1=$vg2
++pool2=$vg3
++DEV="/dev/mapper"
++
++# cryptsetup will ask for password. If luks extension is used
++# it will ask when creating header and then when opening the device
++pass() {
++ echo -e "${passwd}\n${passwd}"
++}
++
++
++# Create encrypted volume
++pass | ssm create $dev1
++check crypt_vol_field $crypt_vol1 type LUKS1
++check crypt_vol_field $crypt_vol1 device $dev1
++check list_table "$(ssm list vol)" $crypt_vol1 $SSM_CRYPT_DEFAULT_POOL none crypt
++
++pass | ssm create $dev2 -e
++check crypt_vol_field $crypt_vol2 type LUKS1
++check crypt_vol_field $crypt_vol2 device $dev2
++check list_table "$(ssm list vol)" $crypt_vol2 $SSM_CRYPT_DEFAULT_POOL none crypt
++
++pass | ssm create -e luks $dev3
++check crypt_vol_field $crypt_vol3 type LUKS1
++check crypt_vol_field $crypt_vol3 device $dev3
++check list_table "$(ssm list vol)" $crypt_vol3 $SSM_CRYPT_DEFAULT_POOL none crypt
++
++pass | ssm create --fs ext4 -e plain $dev4 $mnt1
++check mountpoint $crypt_vol4 $mnt1
++check crypt_vol_field $crypt_vol4 type PLAIN
++check crypt_vol_field $crypt_vol4 device $dev4
++check list_table "$(ssm list vol)" $crypt_vol4 $SSM_CRYPT_DEFAULT_POOL none ext4 none none crypt
++ssm list
++umount $mnt1
++ssm -f remove ${DEV}/$crypt_vol1
++
++pass | ssm create --fs ext3 -s 50M -e plain $dev1 $mnt1
++check mountpoint $crypt_vol1 $mnt1
++check crypt_vol_field $crypt_vol1 type PLAIN
++check crypt_vol_field $crypt_vol1 device $dev1
++check crypt_vol_field $crypt_vol1 size 102400
++check list_table "$(ssm list vol)" $crypt_vol1 $SSM_CRYPT_DEFAULT_POOL none ext3 none none crypt
++umount $mnt1
++
++ssm remove ${DEV}/$crypt_vol1 ${DEV}/$crypt_vol3 ${DEV}/$crypt_vol2 ${DEV}/$crypt_vol4
++
++# Try non existing extension
++not ssm create -e enigma $dev1
++
++# Create encrypted lvm volume
++export SSM_LVM_DEFAULT_POOL=${vg1}_lvm
++export LVOL_PREFIX="lvol"
++lvol1=${LVOL_PREFIX}001
++lvol2=${LVOL_PREFIX}002
++lvol3=${LVOL_PREFIX}003
++lvol4=${LVOL_PREFIX}004
++export SSM_DEFAULT_BACKEND='lvm'
++
++pass | ssm create --fs xfs $dev1 $dev2 $mnt1 -e
++check mountpoint $crypt_vol1 $mnt1
++check crypt_vol_field $crypt_vol1 type LUKS1
++check crypt_vol_field $crypt_vol1 device ${SSM_LVM_DEFAULT_POOL}-$lvol1
++check list_table "$(ssm list vol)" $crypt_vol1 $SSM_CRYPT_DEFAULT_POOL none xfs none none crypt
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL none linear
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol1 pv_count 2
++
++pass | ssm create -r0 $dev3 $dev4 -e plain
++check crypt_vol_field $crypt_vol2 type PLAIN
++check crypt_vol_field $crypt_vol2 device ${SSM_LVM_DEFAULT_POOL}-$lvol2
++check list_table "$(ssm list vol)" $crypt_vol2 $SSM_CRYPT_DEFAULT_POOL none crypt
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol2 $SSM_LVM_DEFAULT_POOL none striped
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol2 pv_count 4
++
++pass | ssm create $dev5 -e luks
++check crypt_vol_field $crypt_vol3 type LUKS1
++check crypt_vol_field $crypt_vol3 device ${SSM_LVM_DEFAULT_POOL}-$lvol3
++check list_table "$(ssm list vol)" $crypt_vol3 $SSM_CRYPT_DEFAULT_POOL none crypt
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol3 $SSM_LVM_DEFAULT_POOL none linear
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol3 pv_count 5
++
++pass | ssm create -e plain --fs xfs -r10 -s ${DEV_SIZE}M $dev6 $dev7 $dev8 $dev9 $mnt2
++check mountpoint $crypt_vol4 $mnt2
++check crypt_vol_field $crypt_vol4 type PLAIN
++check crypt_vol_field $crypt_vol4 device ${SSM_LVM_DEFAULT_POOL}-$lvol4
++check list_table "$(ssm list vol)" $crypt_vol4 $SSM_CRYPT_DEFAULT_POOL 104.00M xfs none none crypt
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol4 $SSM_LVM_DEFAULT_POOL 104.00M raid10
++check lv_field $SSM_LVM_DEFAULT_POOL/$lvol4 pv_count 9
++
++ssm list
++umount $mnt1
++umount $mnt2
++ssm -f remove ${DEV}/$crypt_vol1 ${DEV}/$crypt_vol3 ${DEV}/$crypt_vol2 ${DEV}/$crypt_vol4
++ssm -f remove $SSM_LVM_DEFAULT_POOL
++
++ssm create $dev1 $dev2
++ssm list
++pass | ssm -b crypt create $DM_DEV_DIR/$SSM_LVM_DEFAULT_POOL/$lvol1
++check crypt_vol_field $crypt_vol1 type LUKS1
++check crypt_vol_field $crypt_vol1 device ${SSM_LVM_DEFAULT_POOL}-$lvol1
++check list_table "$(ssm list vol)" $crypt_vol1 $SSM_CRYPT_DEFAULT_POOL none crypt
++check list_table "$(ssm list vol)" $SSM_LVM_DEFAULT_POOL/$lvol1 $SSM_LVM_DEFAULT_POOL none linear
++
++ssm remove ${DEV}/$crypt_vol1
++ssm -f remove $SSM_LVM_DEFAULT_POOL
+diff --git a/tests/bashtests/013-lvm-check.sh b/tests/bashtests/013-lvm-check.sh
+new file mode 100644
+index 0000000..11a70cb
+--- /dev/null
++++ b/tests/bashtests/013-lvm-check.sh
+@@ -0,0 +1,130 @@
++#!/bin/bash
++#
++# (C)2013 Red Hat, Inc., Jimmy Pan <jipan at redhat.com>
++#
++# This program is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++
++export test_name='013-lvm-check'
++export test_description='Exercise ssm lvm check'
++
++. lib/test
++
++DEV_COUNT=10
++DEV_SIZE=100
++aux prepare_devs $DEV_COUNT $DEV_SIZE
++TEST_DEVS=$(cat DEVICES)
++export SSM_DEFAULT_BACKEND='lvm'
++export LVOL_PREFIX="lvol"
++export SSM_LVM_DEFAULT_POOL=$vg1
++export SSM_NONINTERACTIVE='1'
++lvol1=${LVOL_PREFIX}001
++lvol2=${LVOL_PREFIX}002
++lvol3=${LVOL_PREFIX}003
++
++
++pool1=$vg2
++pool2=$vg3
++
++TEST_FS=
++which mkfs.ext2 && grep -E "^\sext[234]$" /proc/filesystems && TEST_FS+="ext2 "
++which mkfs.ext3 && grep -E "^\sext[34]$" /proc/filesystems && TEST_FS+="ext3 "
++which mkfs.ext4 && grep -E "^\sext4$" /proc/filesystems && TEST_FS+="ext4 "
++which mkfs.xfs && grep -E "^\sxfs$" /proc/filesystems && TEST_FS+="xfs"
++
++filesystem_check()
++{
++ if [ $# -lt 3 ] ; then
++ echo Usage: filesystem_check pool volume filesystem
++ exit 1
++ fi
++ local pool=$1
++ local vol=$2
++ local fs=$3
++ local name=$pool/$vol
++ local path=`lvs --noheadings -o lv_path $name`
++ if [[ $fs == ext[234] ]] ; then
++ e2fsck -n $path
++ elif [[ $fs == xfs ]] ; then
++ xfs_repair -n $path
++ else
++ echo Invalid fs type
++ exit 1
++ fi
++}
++
++
++# No filesystem should fail
++ssm create $dev1
++not ssm check $SSM_LVM_DEFAULT_POOL/$lvol1
++ssm -f remove -a
++
++# Loop for different filesystems
++for fs in $TEST_FS ; do
++ # Test check of a volume with one device
++ ssm create --fs $fs -p $SSM_LVM_DEFAULT_POOL $dev1
++ ret1=0
++ filesystem_check $SSM_LVM_DEFAULT_POOL $lvol1 $fs || ret1=$?
++ ret2=0
++ ssm check $SSM_LVM_DEFAULT_POOL/$lvol1 || ret2=$?
++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
++ ret2=0
++ ssm check `lvs --noheadings -o lv_path $SSM_LVM_DEFAULT_POOL/$lvol1` || ret2=$?
++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
++
++ # Test check of a volume with one device with one device has specified
++ # size and on two devices
++ ssm create -p $pool1 --fs $fs -s $((DEV_SIZE+20))m $dev{2,3}
++ ret1=0
++ filesystem_check $pool1 $lvol1 $fs || ret1=$?
++ ret2=0
++ ssm check $pool1/$lvol1 | ret2=$?
++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
++ ret2=0
++ ssm check `lvs --noheadings -o lv_path $pool1/$lvol1` || ret2=$?
++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
++
++ # Test check of volumes from the same pools
++ ssm create -p $pool2 --fs $fs $dev{4,5,6}
++ ssm create --fs $fs $dev{7,8} -p $pool2
++ ret1=0
++ filesystem_check $pool2 $lvol1 $fs || ret1=$?
++ ret2=0
++ ssm check $pool2/$lvol1 || ret2=$?
++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
++ ret2=0
++ ssm check `lvs --noheadings -o lv_path $pool2/$lvol1` || ret2=$?
++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
++ ret1=0
++ filesystem_check $pool2 $lvol2 $fs || ret1=$?
++ ret2=0
++ ssm check $pool2/$lvol2 || ret2=$?
++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
++
++ # Test check of volumes from different pools at once
++ ret2=0
++ ssm check `lvs --noheadings -o lv_path $pool2/$lvol2` || ret2=$?
++ test "$ret1" -eq "$ret2" || [[ "$ret1" -ne 0 && "$ret2" -ne 0 ]]
++ vols="$SSM_LVM_DEFAULT_POOL/$lvol1 $pool1/$lvol1 $pool2/$lvol1 $pool2/$lvol2"
++ ssm check $vols | not grep -i fail
++ ssm check `lvs --noheadings -o lv_path $vols` | not grep -i fail
++
++ # Check invalid volume name should fail
++ not ssm check $pool2/$lvol2"nsdf"
++ ssm -f remove -a
++done
++
++# Some should fail
++not ssm check
++not ssm check non_existing
+diff --git a/tests/bashtests/lib/aux.sh b/tests/bashtests/lib/aux.sh
+index 6add5c3..11a101f 100644
+--- a/tests/bashtests/lib/aux.sh
++++ b/tests/bashtests/lib/aux.sh
+@@ -347,6 +347,7 @@ activation/polling_interval = 0
+ activation/snapshot_autoextend_percent = 50
+ activation/snapshot_autoextend_threshold = 50
+ activation/monitoring = 0
++global/use_lvmetad = 0
+ EOF
+ }
+
+diff --git a/tests/bashtests/lib/check.sh b/tests/bashtests/lib/check.sh
+index 31d8c2d..9a8f853 100644
+--- a/tests/bashtests/lib/check.sh
++++ b/tests/bashtests/lib/check.sh
+@@ -292,6 +292,35 @@ pvlv_counts()
+ vg_field $local_vg snap_count $num_snaps
+ }
+
++crypt_vol_field()
++{
++ data=$(cryptsetup status $1 2> /dev/null | grep $2 | sed -e 's/^[ \t]*//' || true)
++
++ expected=$3
++ case $2 in
++ "type")
++ actual=$(echo ${data##*type:} | sed -e 's/^[ \t]*//') ;;
++ "device")
++ actual=$(echo ${data##*device:} | sed -e 's/^[ \t]*//')
++ actual=$(basename $actual)
++ expected=$(basename $expected)
++ ;;
++ "size")
++ actual=$(echo ${data##*size:} | sed -e 's/^[ \t]*//')
++ actual=${actual%% sectors}
++ ;;
++ *)
++ echo "Unknown field $2"
++ exit 1
++ ;;
++ esac
++
++ test "$actual" = "$expected" || {
++ echo "crypt_vol_field: volume=$1, field=$2, actual=$actual, expected=$expected"
++ exit 1
++ }
++}
++
+ btrfs_fs_field()
+ {
+ lines=$(btrfs filesystem show 2> /dev/null | grep -A 1 "'$1'" || true)
+diff --git a/tests/bashtests/set.sh b/tests/bashtests/set.sh
+index 1306a4e..d2fcf7d 100755
+--- a/tests/bashtests/set.sh
++++ b/tests/bashtests/set.sh
+@@ -15,6 +15,7 @@
+ # You should have received a copy of the GNU General Public License
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
+
++chmod +x *.sh
+ for i in $(ls lib/*.sh); do
+ echo "$i -> ${i%%.sh}"
+ cp $i ${i%%.sh}
+diff --git a/tests/unittests/common.py b/tests/unittests/common.py
+index ec60ae2..58c5637 100644
+--- a/tests/unittests/common.py
++++ b/tests/unittests/common.py
+@@ -74,8 +74,8 @@ class MockSystemDataSource(unittest.TestCase):
+ misc.run = self.mock_run
+ self.get_partitions_orig = misc.get_partitions
+ misc.get_partitions = self.mock_get_partitions
+- self.is_bdevice_orig = main.is_bdevice
+- main.is_bdevice = self.mock_is_bdevice
++ self.is_bdevice_orig = misc.is_bdevice
++ misc.is_bdevice = self.mock_is_bdevice
+ self.is_directory_orig = main.is_directory
+ main.is_directory = self.mock_is_directory
+ self.check_create_item_orig = main.StorageHandle.check_create_item
+@@ -88,6 +88,8 @@ class MockSystemDataSource(unittest.TestCase):
+ misc.check_binary = self.mock_check_binary
+ self.send_udev_event_orig = misc.send_udev_event
+ misc.send_udev_event = self.mock_send_udev_event
++ self.get_fs_type_orig = misc.get_fs_type
++ misc.get_fs_type = self.mock_get_fs_type
+ self.dev_data = {}
+ self.vol_data = {}
+ self.pool_data = {}
+@@ -104,25 +106,32 @@ class MockSystemDataSource(unittest.TestCase):
+ self.mount_data = {}
+ misc.run = self.run_orig
+ misc.get_partitions = self.get_partitions_orig
+- main.is_bdevice = self.is_bdevice_orig
++ misc.is_bdevice = self.is_bdevice_orig
+ main.is_directory = self.is_directory_orig
+ misc.get_mounts = self.get_mounts_orig
+ main.StorageHandle.check_create_item = self.check_create_item_orig
+ misc.temp_mount = self.temp_mount_orig
+ misc.check_binary = self.check_binary_orig
+ misc.send_udev_event = self.send_udev_event_orig
++ misc.get_fs_type = self.get_fs_type_orig
+ main.SSM_NONINTERACTIVE = False
+
+ def _cmdEq(self, out, index=-1):
+ self.assertEqual(self.run_data[index], out)
+
+- def _checkCmd(self, command, args, expected=None):
++ def _cmdNotEq(self, out, index=-1):
++ self.assertNotEqual(self.run_data[index], out)
++
++ def _checkCmd(self, command, args, expected=None, NotEq=False):
+ self.run_data = []
+ for case in misc.permutations(args):
+ cmd = command + " " + " ".join(case)
+ main.main(cmd)
+ if expected:
+- self._cmdEq(expected)
++ if NotEq:
++ self._cmdNotEq(expected)
++ else:
++ self._cmdEq(expected)
+
+ def mock_run(self, cmd, *args, **kwargs):
+ self.run_data.append(" ".join(cmd))
+@@ -166,7 +175,21 @@ class MockSystemDataSource(unittest.TestCase):
+ if path in self.directories:
+ self._mpoint = path
+ return
+- return main.is_bdevice(path)
++ return misc.is_bdevice(path)
++
++ def mock_get_fs_type(self, device):
++ if device in self.vol_data:
++ if 'fstype' in self.vol_data[device]:
++ return self.vol_data[device]['fstype']
++ else:
++ return None
++ elif device in self.dev_data:
++ if 'fstype' in self.dev_data[device]:
++ return self.dev_data[device]['fstype']
++ else:
++ return None
++ else:
++ return None
+
+ def mock_send_udev_event(self, device, event):
+ pass
+@@ -211,6 +234,8 @@ class MockSystemDataSource(unittest.TestCase):
+ pool_data = self.pool_data[pool_name]
+ pool_free = float(pool_data['pool_free']) - vol_size
+ pool_used = float(pool_data['pool_used']) + vol_size
++ pool_data['pool_free'] = pool_free
++ pool_data['pool_used'] = pool_used
+ if mount:
+ self.pool_data[pool_name]['mount'] = mount
+ self._addDir(mount)
+diff --git a/tests/unittests/test_btrfs.py b/tests/unittests/test_btrfs.py
+index b755414..d1f8135 100644
+--- a/tests/unittests/test_btrfs.py
++++ b/tests/unittests/test_btrfs.py
+@@ -30,11 +30,11 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ self._addDevice('/dev/sda', 11489037516)
+ self._addDevice('/dev/sdb', 234566451)
+ self._addDevice('/dev/sdc', 2684354560)
+- self._addDevice('/dev/sdc1', 894784853, 1)
++ self._addDevice('/dev/sdc1', 2042177280, 1)
+ self._addDevice('/dev/sdc2', 29826161, 2)
+ self._addDevice('/dev/sdc3', 1042177280, 3)
+ self._addDevice('/dev/sdd', 11673)
+- self._addDevice('/dev/sde', 1042177280)
++ self._addDevice('/dev/sde', 1073741824)
+ main.SSM_DEFAULT_BACKEND = 'btrfs'
+
+ self.check_new_path_orig = btrfs.BtrfsPool._check_new_path
+@@ -118,8 +118,8 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda".format(default_pool))
+ self._checkCmd("ssm create", ['-r 0', '-s 2.6T', '/dev/sda'],
+ "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda".format(default_pool))
+- self._checkCmd("ssm create", ['-r 1', '-s 512k', '/dev/sda'],
+- "mkfs.btrfs -L btrfs_pool -m raid1 -d raid1 -b 524288 -f /dev/sda".format(default_pool))
++ self._checkCmd("ssm create", ['-r 1', '-s 512k', '/dev/sda /dev/sdb'],
++ "mkfs.btrfs -L btrfs_pool -m raid1 -d raid1 -b 524288 -f /dev/sda /dev/sdb".format(default_pool))
+ self._checkCmd("ssm create", ['-r 10', '-s 10M', '/dev/sda'],
+ "mkfs.btrfs -L btrfs_pool -m raid10 -d raid10 -b 10485760 -f /dev/sda".format(default_pool))
+
+@@ -244,6 +244,8 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ "btrfs subvolume snapshot /mnt/mount /mnt/mount/new_snap")
+
+ def test_btrfs_resize(self):
++ # Btrfs resize not supported for now because it simply does not work
++ return
+ # Generate some storage data
+ self._addPool('default_pool', ['/dev/sda', '/dev/sdb'])
+ self._addPool('my_pool', ['/dev/sdc2', '/dev/sdc3'])
+@@ -265,9 +267,16 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ "btrfs filesystem resize 11723608063K /tmp/mount")
+ main.SSM_DEFAULT_BACKEND = 'btrfs'
+
+- self._cmdEq("btrfs device add /dev/sde /tmp/mount", -2)
++ self._cmdNotEq("btrfs device add /dev/sde /tmp/mount", -2)
++
++ # Resize with enough space in the pool
+ self._checkCmd("ssm resize --size +1g", ['my_pool /dev/sde'],
+ "btrfs filesystem resize 1073052017K /mnt/test1")
++ self._cmdNotEq("btrfs device add /dev/sde /mnt/test1", -2)
++
++ # Resize without enough space in the pool
++ self._checkCmd("ssm resize --size +1t", ['my_pool /dev/sde'],
++ "btrfs filesystem resize 2145745265K /mnt/test1")
+ self._cmdEq("btrfs device add /dev/sde /mnt/test1", -2)
+
+ # Shrink volume
+@@ -285,16 +294,23 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ "btrfs filesystem resize 10240K /mnt/test1")
+
+ # Set volume and add devices
+- self._checkCmd("ssm resize -s 12T default_pool /dev/sdc1 /dev/sde",
+- [], "btrfs filesystem resize 12884901888K /tmp/mount")
++ self._checkCmd("ssm resize -s 20T default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 21474836480K /tmp/mount")
++ self.assertNotEqual(self.run_data[-2],
++ "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++
++ self._checkCmd("ssm resize -s 22T default_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 23622320128K /tmp/mount")
+ self.assertEqual(self.run_data[-2],
+ "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
+- self._checkCmd("ssm resize -s 1T my_pool /dev/sdc1 /dev/sde",
+- [], "btrfs filesystem resize 1073741824K /mnt/test1")
++
++ self._checkCmd("ssm resize -s 3T my_pool /dev/sdc1 /dev/sde",
++ [], "btrfs filesystem resize 3221225472K /mnt/test1")
+ self.assertEqual(self.run_data[-2],
+ "btrfs device add /dev/sdc1 /dev/sde /mnt/test1")
+- self._checkCmd("ssm resize -s 1T my_pool /dev/sde /dev/sdc2",
+- [], "btrfs filesystem resize 1073741824K /mnt/test1")
++
++ self._checkCmd("ssm resize -s 3T my_pool /dev/sde /dev/sdc2",
++ [], "btrfs filesystem resize 3221225472K /mnt/test1")
+ self.assertEqual(self.run_data[-2],
+ "btrfs device add /dev/sde /mnt/test1")
+
+@@ -308,6 +324,8 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ self.assertNotEqual(self.run_data[-2],
+ "btrfs device add /dev/sdc1 /dev/sde /mnt/test1")
+
++ self._checkCmd("ssm list",
++ [], "btrfs filesystem resize 12247891967K /tmp/mount")
+ # Extend volume and add devices
+ self._checkCmd("ssm resize -s +500G default_pool /dev/sdc1 /dev/sde",
+ [], "btrfs filesystem resize 12247891967K /tmp/mount")
+diff --git a/tests/unittests/test_lvm.py b/tests/unittests/test_lvm.py
+index 6f63fae..7af8a78 100644
+--- a/tests/unittests/test_lvm.py
++++ b/tests/unittests/test_lvm.py
+@@ -287,10 +287,16 @@ class LvmFunctionCheck(MockSystemDataSource):
+ self.assertEqual(self.run_data[-2],
+ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
+
+- # Set volume size
++ # Set volume size with sufficient amount of space
+ self._checkCmd("ssm resize -s 10G /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
+ [], "lvm lvresize -L 10485760.0k /dev/default_pool/vol003")
+- self.assertEqual(self.run_data[-2],
++ self.assertNotEqual(self.run_data[-2],
++ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
++
++ # Set volume size without sufficient amount of space
++ self._checkCmd("ssm resize -s 10T /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
++ [], "lvm lvresize -L 10737418240.0k /dev/default_pool/vol003")
++ self.assertNotEqual(self.run_data[-2],
+ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
+
+ # Extend volume and add devices
+@@ -299,9 +305,15 @@ class LvmFunctionCheck(MockSystemDataSource):
+ self.assertEqual(self.run_data[-2],
+ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
+
+- # Extend volume
++ # Extend volume with ehough space in pool
+ self._checkCmd("ssm resize -s +10G /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
+ [], "lvm lvresize -L 10486784.0k /dev/default_pool/vol003")
++ self.assertNotEqual(self.run_data[-2],
++ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
++
++ # Extend volume without ehough space in pool
++ self._checkCmd("ssm resize -s +20T /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
++ [], "lvm lvresize -L 21474837504.0k /dev/default_pool/vol003")
+ self.assertEqual(self.run_data[-2],
+ "lvm vgextend default_pool /dev/sdc1 /dev/sde")
+
+diff --git a/tests/unittests/test_ssm.py b/tests/unittests/test_ssm.py
+index 9a5d82d..e0a1d99 100644
+--- a/tests/unittests/test_ssm.py
++++ b/tests/unittests/test_ssm.py
+@@ -365,12 +365,12 @@ class SsmFunctionCheck(MockSystemDataSource):
+ # Set volume size
+ self._checkCmd("ssm resize -s 10G /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
+ [], "vol resize /dev/default_pool/vol003 10485760.0 False")
+- self.assertEqual(self.run_data[-2],
++ self.assertNotEqual(self.run_data[-2],
+ "pool extend default_pool /dev/sdc1 /dev/sde")
+
+ # Extend volume size with adding more devices
+- self._checkCmd("ssm resize -s +10G /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
+- [], "vol resize /dev/default_pool/vol003 10486784.0 False")
++ self._checkCmd("ssm resize -s +12t /dev/default_pool/vol003 /dev/sdc1 /dev/sde",
++ [], "vol resize /dev/default_pool/vol003 12884902912.0 False")
+ self.assertEqual(self.run_data[-2],
+ "pool extend default_pool /dev/sdc1 /dev/sde")
+
+@@ -428,9 +428,9 @@ class SsmFunctionCheck(MockSystemDataSource):
+ # Number of stripes must not exceed number of devices
+ self.assertRaises(problem.GeneralError, main.main, "ssm create -r 1 -s 2.6T -I 16 -i 4 /dev/sda")
+
+- self._checkCmd("ssm create", ['-r 1', '-s 2.6T', '-I 16', '/dev/sda'],
+- "pool create {0} 2791728742.40 1 16 /dev/sda".format(main.DEFAULT_DEVICE_POOL))
+- self._cmdEq("pool new {0} /dev/sda".format(main.DEFAULT_DEVICE_POOL), -2)
++ self._checkCmd("ssm create", ['-r 1', '-s 2.6T', '-I 16', '/dev/sda /dev/sdb'],
++ "pool create {0} 2791728742.40 1 16 /dev/sda /dev/sdb".format(main.DEFAULT_DEVICE_POOL))
++ self._cmdEq("pool new {0} /dev/sda /dev/sdb".format(main.DEFAULT_DEVICE_POOL), -2)
+
+ # Create volume using single device from non existent my_pool
+ self._checkCmd("ssm create", ['--pool my_pool', '/dev/sda'],
+@@ -760,13 +760,14 @@ class PoolInfo(MyInfo):
+ misc.run(cmd)
+
+ def create(self, pool, size='', name='', devs='',
+- raid=None):
++ options=None):
++ options = options or {}
+ if type(devs) is not list:
+ devices = [devs]
+- if raid:
+- stripes = raid['stripes']
+- stripesize = raid['stripesize']
+- level = raid['level']
++ if 'raid' in options:
++ stripes = options['stripes']
++ stripesize = options['stripesize']
++ level = options['raid']
+ else:
+ stripes = stripesize = level = ""
+ misc.run([self.f, self.v, self.y, 'pool create', pool, size, name,
diff --git a/ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch b/ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch
new file mode 100644
index 0000000..6d5cf6e
--- /dev/null
+++ b/ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch
@@ -0,0 +1,31 @@
+From dbc8ab76b9f50048f8cf73a792cfded7c1061023 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Wed, 15 Jan 2014 12:19:08 +0100
+Subject: [PATCH 02/10] ssm: btrfs backend: try to find the real device
+
+Currently btrfs backend is trying to use the "device" directly from the
+output. However in recent btrfs it has been changed to print out device
+names in format (/dev/mapper/whatever) so we should really use
+get_real_device() function to obtain the "real" device name.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/backends/btrfs.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
+index eb23403..750a517 100644
+--- a/ssmlib/backends/btrfs.py
++++ b/ssmlib/backends/btrfs.py
+@@ -119,7 +119,7 @@ class Btrfs(template.Backend):
+ fs_used = float(misc.get_real_size(array[6]))
+
+ elif array[0] == 'devid':
+- dev['dev_name'] = array[7]
++ dev['dev_name'] = misc.get_real_device(array[7])
+
+ if not pool_name:
+ pool_name = self._find_uniq_pool_name(label, array[7])
+--
+1.8.3.1
+
diff --git a/ssm-0.4-ssm-force-btrfs-device-add.patch b/ssm-0.4-ssm-force-btrfs-device-add.patch
new file mode 100644
index 0000000..fd8d80f
--- /dev/null
+++ b/ssm-0.4-ssm-force-btrfs-device-add.patch
@@ -0,0 +1,341 @@
+From e5ca9b110954900f30bf32d66ea3440a4203e84c Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Wed, 15 Jan 2014 13:59:44 +0100
+Subject: [PATCH 03/10] ssm: force btrfs device add
+
+Btrfs is broken. It does not recognize the btrfs signature properly and
+even when the device has been wiped with wipefs btrfs would still claim
+that the device actually is 'btrfs' device which it is not!
+
+We've already fixed the case for mkfs.btrfs, howeve btrfs recently added
+the check for 'btrfs device add' as well, so we need to do the same
+thing. So make _can_btrfs_force() more generic and use it in extend()
+method as well.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/backends/btrfs.py | 19 +++++---
+ tests/unittests/test_btrfs.py | 102 +++++++++++++++++++++---------------------
+ 2 files changed, 65 insertions(+), 56 deletions(-)
+
+diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
+index 750a517..b238d52 100644
+--- a/ssmlib/backends/btrfs.py
++++ b/ssmlib/backends/btrfs.py
+@@ -411,14 +411,13 @@ class BtrfsPool(Btrfs, template.BackendPool):
+ else:
+ self.data = self._pool
+
+- def _can_btrfs_force(self):
++ def _can_btrfs_force(self, command):
+ """
+ This is just ridiculous. Unfortunately btrfs tools usually change
+ behaviour and options without bumping version number. So we have
+ to check whether btrfs allows to 'force' file system creation.
+ """
+- command=['mkfs.btrfs', '-f']
+- output = misc.run(command, can_fail=True)[1]
++ output = misc.run(command + ['--force'], can_fail=True)[1]
+ found = re.search('invalid option', output)
+ if found:
+ return False
+@@ -456,8 +455,8 @@ class BtrfsPool(Btrfs, template.BackendPool):
+ # have tried to remove the device from the respective pool already.
+ # So at this point there should not be any useful signatures to
+ # speak of. However as I mentioned btrfs is broken, so force it.
+- if self._can_btrfs_force():
+- command.extend(['-f'])
++ if self._can_btrfs_force(command):
++ command.extend(['--force'])
+ command.extend(devs)
+ misc.run(command, stdout=True)
+ misc.send_udev_event(devs[0], "change")
+@@ -499,6 +498,16 @@ class BtrfsPool(Btrfs, template.BackendPool):
+ if type(devices) is not list:
+ devices = [devices]
+ command = ['device', 'add']
++ # This might seem weird, but btrfs is mostly broken when it comes to
++ # checking existing signatures because it will for example check for
++ # backup superblocks as well, which is wrong. Also we have check for
++ # existing file system signatures in the ssm itself. Other things
++ # than file system should be covered by the backend and we should
++ # have tried to remove the device from the respective pool already.
++ # So at this point there should not be any useful signatures to
++ # speak of. However as I mentioned btrfs is broken, so force it.
++ if self._can_btrfs_force(['btrfs', 'device', 'add']):
++ command.extend(['--force'])
+ command.extend(devices)
+ command.append(pool['mount'])
+ self.run_btrfs(command)
+diff --git a/tests/unittests/test_btrfs.py b/tests/unittests/test_btrfs.py
+index d1f8135..2fb04bc 100644
+--- a/tests/unittests/test_btrfs.py
++++ b/tests/unittests/test_btrfs.py
+@@ -91,48 +91,48 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+
+ # Create volume using single device from non existent default pool
+ self._checkCmd("ssm create", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+
+ # Specify default backend
+ self._checkCmd("ssm -b btrfs create", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+
+ main.SSM_DEFAULT_BACKEND = 'lvm'
+ self._checkCmd("ssm --backend btrfs create", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+ main.SSM_DEFAULT_BACKEND = 'btrfs'
+
+- self._checkCmd("ssm -f create", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ self._checkCmd("ssm --force create", ['/dev/sda'],
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+
+ self._checkCmd("ssm -v create", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+
+- self._checkCmd("ssm -f -v create", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ self._checkCmd("ssm --force -v create", ['/dev/sda'],
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+
+ self._checkCmd("ssm create", ['-s 2.6T', '/dev/sda'],
+- "mkfs.btrfs -L {0} -b 2858730232217 -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L {0} -b 2858730232217 --force /dev/sda".format(default_pool))
+
+ self._checkCmd("ssm create", ['-r 0', '-s 2.6T', '/dev/sda'],
+- "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 --force /dev/sda".format(default_pool))
+ self._checkCmd("ssm create", ['-r 0', '-s 2.6T', '/dev/sda'],
+- "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L btrfs_pool -m raid0 -d raid0 -b 2858730232217 --force /dev/sda".format(default_pool))
+ self._checkCmd("ssm create", ['-r 1', '-s 512k', '/dev/sda /dev/sdb'],
+- "mkfs.btrfs -L btrfs_pool -m raid1 -d raid1 -b 524288 -f /dev/sda /dev/sdb".format(default_pool))
++ "mkfs.btrfs -L btrfs_pool -m raid1 -d raid1 -b 524288 --force /dev/sda /dev/sdb".format(default_pool))
+ self._checkCmd("ssm create", ['-r 10', '-s 10M', '/dev/sda'],
+- "mkfs.btrfs -L btrfs_pool -m raid10 -d raid10 -b 10485760 -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L btrfs_pool -m raid10 -d raid10 -b 10485760 --force /dev/sda".format(default_pool))
+
+ # Create volume using single device from non existent my_pool
+ self._checkCmd("ssm create", ['--pool my_pool', '/dev/sda'],
+- "mkfs.btrfs -L my_pool -f /dev/sda")
++ "mkfs.btrfs -L my_pool --force /dev/sda")
+
+ self._checkCmd("ssm create", ['-p my_pool', '-r 0', '-s 2.6T', '/dev/sda'],
+- "mkfs.btrfs -L my_pool -m raid0 -d raid0 -b 2858730232217 -f /dev/sda")
++ "mkfs.btrfs -L my_pool -m raid0 -d raid0 -b 2858730232217 --force /dev/sda")
+
+ # Create volume using multiple devices
+ self._checkCmd("ssm create /dev/sda /dev/sdb", [],
+- "mkfs.btrfs -L {0} -f /dev/sda /dev/sdb".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda /dev/sdb".format(default_pool))
+
+ # Create volume using single device from existing pool
+ self._addPool(default_pool, ['/dev/sdb', '/dev/sdd'])
+@@ -151,15 +151,15 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ # in the pool
+ self._checkCmd("ssm create", ['-n myvolume', '/dev/sda /dev/sdb'],
+ "btrfs subvolume create /tmp/mount/myvolume")
+- self._cmdEq("btrfs device add /dev/sda /tmp/mount", -2)
++ self._cmdEq("btrfs device add --force /dev/sda /tmp/mount", -2)
+
+ self._checkCmd("ssm create", ['-p my_pool', '-n myvolume', '/dev/sdc2 /dev/sda'],
+ "btrfs subvolume create /tmp/mount/myvolume")
+- self._cmdEq("btrfs device add /dev/sda /mnt/test", -2)
++ self._cmdEq("btrfs device add --force /dev/sda /mnt/test", -2)
+
+ self._checkCmd("ssm create", ['-n myvolume', '/dev/sda /dev/sdb /dev/sde'],
+ "btrfs subvolume create /tmp/mount/myvolume")
+- self._cmdEq("btrfs device add /dev/sda /dev/sde /tmp/mount", -2)
++ self._cmdEq("btrfs device add --force /dev/sda /dev/sde /tmp/mount", -2)
+
+ def test_btrfs_remove(self):
+ # Generate some storage data
+@@ -267,17 +267,17 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ "btrfs filesystem resize 11723608063K /tmp/mount")
+ main.SSM_DEFAULT_BACKEND = 'btrfs'
+
+- self._cmdNotEq("btrfs device add /dev/sde /tmp/mount", -2)
++ self._cmdNotEq("btrfs device add --force /dev/sde /tmp/mount", -2)
+
+ # Resize with enough space in the pool
+ self._checkCmd("ssm resize --size +1g", ['my_pool /dev/sde'],
+ "btrfs filesystem resize 1073052017K /mnt/test1")
+- self._cmdNotEq("btrfs device add /dev/sde /mnt/test1", -2)
++ self._cmdNotEq("btrfs device add --force /dev/sde /mnt/test1", -2)
+
+ # Resize without enough space in the pool
+ self._checkCmd("ssm resize --size +1t", ['my_pool /dev/sde'],
+ "btrfs filesystem resize 2145745265K /mnt/test1")
+- self._cmdEq("btrfs device add /dev/sde /mnt/test1", -2)
++ self._cmdEq("btrfs device add --force /dev/sde /mnt/test1", -2)
+
+ # Shrink volume
+ self._checkCmd("ssm resize", ['-s-100G', 'default_pool'],
+@@ -285,7 +285,7 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ self._checkCmd("ssm resize -s-500G", ['my_pool /dev/sde'],
+ "btrfs filesystem resize 547715441K /mnt/test1")
+ self.assertNotEqual(self.run_data[-2],
+- "btrfs device add /dev/sde /mnt/test1")
++ "btrfs device add --force /dev/sde /mnt/test1")
+
+ # Set volume size
+ self._checkCmd("ssm resize", ['-s 10M', 'default_pool'],
+@@ -297,32 +297,32 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ self._checkCmd("ssm resize -s 20T default_pool /dev/sdc1 /dev/sde",
+ [], "btrfs filesystem resize 21474836480K /tmp/mount")
+ self.assertNotEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
+
+ self._checkCmd("ssm resize -s 22T default_pool /dev/sdc1 /dev/sde",
+ [], "btrfs filesystem resize 23622320128K /tmp/mount")
+ self.assertEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
+
+ self._checkCmd("ssm resize -s 3T my_pool /dev/sdc1 /dev/sde",
+ [], "btrfs filesystem resize 3221225472K /mnt/test1")
+ self.assertEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /mnt/test1")
++ "btrfs device add --force /dev/sdc1 /dev/sde /mnt/test1")
+
+ self._checkCmd("ssm resize -s 3T my_pool /dev/sde /dev/sdc2",
+ [], "btrfs filesystem resize 3221225472K /mnt/test1")
+ self.assertEqual(self.run_data[-2],
+- "btrfs device add /dev/sde /mnt/test1")
++ "btrfs device add --force /dev/sde /mnt/test1")
+
+ # Set volume in without the need adding more devices
+ self._checkCmd("ssm resize -s 10G default_pool /dev/sdc1 /dev/sde",
+ [], "btrfs filesystem resize 10485760K /tmp/mount")
+ self.assertNotEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
+ self._checkCmd("ssm resize -s 10G my_pool /dev/sdd /dev/sde",
+ [], "btrfs filesystem resize 10485760K /mnt/test1")
+ self.assertNotEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /mnt/test1")
++ "btrfs device add --force /dev/sdc1 /dev/sde /mnt/test1")
+
+ self._checkCmd("ssm list",
+ [], "btrfs filesystem resize 12247891967K /tmp/mount")
+@@ -330,23 +330,23 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ self._checkCmd("ssm resize -s +500G default_pool /dev/sdc1 /dev/sde",
+ [], "btrfs filesystem resize 12247891967K /tmp/mount")
+ self.assertEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
+
+ # Extend volume in without the need adding more devices
+ self._checkCmd("ssm resize -s 1k default_pool /dev/sdc1 /dev/sde",
+ [], "btrfs filesystem resize 1K /tmp/mount")
+ self.assertNotEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
+ self.assertNotEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
+
+ # Shrink volume with devices provided
+ self._checkCmd("ssm resize -s-10G default_pool /dev/sdc1 /dev/sde",
+ [], "btrfs filesystem resize 11713118207K /tmp/mount")
+ self.assertNotEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
+ self.assertNotEqual(self.run_data[-2],
+- "btrfs device add /dev/sdc1 /dev/sde /tmp/mount")
++ "btrfs device add --force /dev/sdc1 /dev/sde /tmp/mount")
+
+ def test_btrfs_add(self):
+ default_pool = btrfs.SSM_BTRFS_DEFAULT_POOL
+@@ -354,39 +354,39 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ # Adding to non existent pool
+ # Add device into default pool
+ self._checkCmd("ssm add", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+
+ # Specify backend
+ self._checkCmd("ssm --backend btrfs add", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+
+ main.SSM_DEFAULT_BACKEND = 'lvm'
+ self._checkCmd("ssm -b btrfs add", ['/dev/sda'],
+- "mkfs.btrfs -L {0} -f /dev/sda".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda".format(default_pool))
+ main.SSM_DEFAULT_BACKEND = 'btrfs'
+
+ # Add more devices into default pool
+ self._checkCmd("ssm add", ['/dev/sda /dev/sdc1'],
+- "mkfs.btrfs -L {0} -f /dev/sda /dev/sdc1".format(default_pool))
++ "mkfs.btrfs -L {0} --force /dev/sda /dev/sdc1".format(default_pool))
+ # Add device into defined pool
+ self._checkCmd("ssm add", ['-p my_pool', '/dev/sda'],
+- "mkfs.btrfs -L my_pool -f /dev/sda")
++ "mkfs.btrfs -L my_pool --force /dev/sda")
+ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sda'],
+- "mkfs.btrfs -L my_pool -f /dev/sda")
++ "mkfs.btrfs -L my_pool --force /dev/sda")
+ # Add more devices into defined pool
+ self._checkCmd("ssm add", ['-p my_pool', '/dev/sda /dev/sdc1'],
+- "mkfs.btrfs -L my_pool -f /dev/sda /dev/sdc1")
++ "mkfs.btrfs -L my_pool --force /dev/sda /dev/sdc1")
+ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sda /dev/sdc1'],
+- "mkfs.btrfs -L my_pool -f /dev/sda /dev/sdc1")
++ "mkfs.btrfs -L my_pool --force /dev/sda /dev/sdc1")
+
+ # Adding to existing default pool
+ self._addPool(default_pool, ['/dev/sdb', '/dev/sdd'])
+ # Add device into default pool
+ self._checkCmd("ssm add", ['/dev/sda'],
+- "btrfs device add /dev/sda /tmp/mount")
++ "btrfs device add --force /dev/sda /tmp/mount")
+ # Add more devices into default pool
+ self._checkCmd("ssm add", ['/dev/sda /dev/sdc1'],
+- "btrfs device add /dev/sda /dev/sdc1 /tmp/mount")
++ "btrfs device add --force /dev/sda /dev/sdc1 /tmp/mount")
+
+ # Adding to existing defined pool
+ self._addPool('my_pool', ['/dev/sdc2', '/dev/sdc3'])
+@@ -394,25 +394,25 @@ class BtrfsFunctionCheck(MockSystemDataSource):
+ '/mnt/test1')
+ # Add device into defined pool
+ self._checkCmd("ssm add", ['-p my_pool', '/dev/sda'],
+- "btrfs device add /dev/sda /mnt/test1")
++ "btrfs device add --force /dev/sda /mnt/test1")
+ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sda'],
+- "btrfs device add /dev/sda /mnt/test1")
++ "btrfs device add --force /dev/sda /mnt/test1")
+ # Add more devices into defined pool
+ self._checkCmd("ssm add", ['-p my_pool', '/dev/sda /dev/sdc1'],
+- "btrfs device add /dev/sda /dev/sdc1 /mnt/test1")
++ "btrfs device add --force /dev/sda /dev/sdc1 /mnt/test1")
+ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sda /dev/sdc1'],
+- "btrfs device add /dev/sda /dev/sdc1 /mnt/test1")
++ "btrfs device add --force /dev/sda /dev/sdc1 /mnt/test1")
+ # Add verbose
+ self._checkCmd("ssm -v add", ['--pool {0}'.format(default_pool),
+ '/dev/sda /dev/sdc1'],
+- "btrfs device add /dev/sda /dev/sdc1 /tmp/mount")
++ "btrfs device add --force /dev/sda /dev/sdc1 /tmp/mount")
+
+ # Add two devices into existing pool (one of the devices already is in
+ # the pool
+ self._checkCmd("ssm add", ['--pool my_pool', '/dev/sdc2 /dev/sda'],
+- "btrfs device add /dev/sda /mnt/test1")
++ "btrfs device add --force /dev/sda /mnt/test1")
+ self._checkCmd("ssm add", ['/dev/sda /dev/sdb'],
+- "btrfs device add /dev/sda /tmp/mount")
++ "btrfs device add --force /dev/sda /tmp/mount")
+
+ def test_btrfs_mount(self):
+ self._addDir("/mnt/test")
+--
+1.8.3.1
+
diff --git a/ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch b/ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch
new file mode 100644
index 0000000..890a04f
--- /dev/null
+++ b/ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch
@@ -0,0 +1,27 @@
+From 0e461e0fb15c47513faf0f27b553624ea51b069b Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Wed, 15 Jan 2014 17:39:00 +0100
+Subject: [PATCH 06/10] test 008: Device count should go down after removing a
+ device
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ tests/bashtests/008-btrfs-remove.sh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tests/bashtests/008-btrfs-remove.sh b/tests/bashtests/008-btrfs-remove.sh
+index fd9b91e..4e40e99 100755
+--- a/tests/bashtests/008-btrfs-remove.sh
++++ b/tests/bashtests/008-btrfs-remove.sh
+@@ -93,7 +93,7 @@ export SSM_DEFAULT_BACKEND='btrfs'
+ not check btrfs_fs_field $pool1 label $pool1
+ not check btrfs_vol_field $mnt1 subvolume $vol1
+ not check btrfs_vol_field $mnt2 subvolume $vol2
+-check btrfs_fs_field $SSM_BTRFS_DEFAULT_POOL dev_count 5
++check btrfs_fs_field $SSM_BTRFS_DEFAULT_POOL dev_count 4
+ umount_all
+ ssm -f remove --all
+
+--
+1.8.3.1
+
diff --git a/ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch b/ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch
new file mode 100644
index 0000000..64c658e
--- /dev/null
+++ b/ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch
@@ -0,0 +1,27 @@
+From a5da549a380d83db0ee4b9bed70ec3575e6c2f37 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Wed, 15 Jan 2014 15:52:34 +0100
+Subject: [PATCH 04/10] tests: Fix they way we gather information about btrfs
+ file system
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ tests/bashtests/lib/check.sh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tests/bashtests/lib/check.sh b/tests/bashtests/lib/check.sh
+index 9a8f853..e9a7f39 100644
+--- a/tests/bashtests/lib/check.sh
++++ b/tests/bashtests/lib/check.sh
+@@ -323,7 +323,7 @@ crypt_vol_field()
+
+ btrfs_fs_field()
+ {
+- lines=$(btrfs filesystem show 2> /dev/null | grep -A 1 "'$1'" || true)
++ lines=$(btrfs filesystem show 2> /dev/null | grep -A 1 "^Label:.*$1" || true)
+
+ case $2 in
+ "label")
+--
+1.8.3.1
+
diff --git a/ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch b/ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch
new file mode 100644
index 0000000..f690ff3
--- /dev/null
+++ b/ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch
@@ -0,0 +1,38 @@
+From c3609263094816eee96bef416f7ed72b49c8f2b8 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner at redhat.com>
+Date: Wed, 15 Jan 2014 16:51:18 +0100
+Subject: [PATCH 05/10] tests: Make btrfs recognize devices from tests suite
+ properly
+
+This is ugly hack to fix a problem with test suite and btrfs
+where ?sometimes? btrfs prints out device name in the path
+of the test suite rather than path in the real '/dev/'
+directory. This should cover that without any impact on
+real usage.
+
+Signed-off-by: Lukas Czerner <lczerner at redhat.com>
+---
+ ssmlib/backends/btrfs.py | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/ssmlib/backends/btrfs.py b/ssmlib/backends/btrfs.py
+index b238d52..836f108 100644
+--- a/ssmlib/backends/btrfs.py
++++ b/ssmlib/backends/btrfs.py
+@@ -119,6 +119,13 @@ class Btrfs(template.Backend):
+ fs_used = float(misc.get_real_size(array[6]))
+
+ elif array[0] == 'devid':
++ # This is ugly hack to fix a problem with test suite and btrfs
++ # where ?sometimes? btrfs prints out device name in the path
++ # of the test suite rather than path in the real '/dev/'
++ # directory. This should cover that without any impact on
++ # real usage
++ if not os.path.islink(array[7]):
++ array[7] = re.sub(r'.*/dev/', '/dev/', array[7])
+ dev['dev_name'] = misc.get_real_device(array[7])
+
+ if not pool_name:
+--
+1.8.3.1
+
diff --git a/system-storage-manager.spec b/system-storage-manager.spec
index a0c7da1..7ffa1a2 100644
--- a/system-storage-manager.spec
+++ b/system-storage-manager.spec
@@ -1,6 +1,6 @@
Name: system-storage-manager
-Version: 0.2
-Release: 2%{?dist}
+Version: 0.4
+Release: 4%{?dist}
Summary: A single tool to manage your storage
Group: System Environment/Base
@@ -8,6 +8,22 @@ License: GPLv2+
URL: http://storagemanager.sf.net
Source0: http://downloads.sourceforge.net/storagemanager/%{name}-%{version}.tar.gz
+
+Patch1: ssm-0.4-ssm-big-update-no-1.patch
+Patch2: ssm-0.4-misc-get_unit_size-accept-different-unit-formats.patch
+Patch3: ssm-0.4-ssm-btrfs-backend-try-to-find-the-real-device.patch
+Patch4: ssm-0.4-ssm-force-btrfs-device-add.patch
+Patch5: ssm-0.4-tests-Fix-they-way-we-gather-information-about-btrfs.patch
+Patch6: ssm-0.4-tests-Make-btrfs-recognize-devices-from-tests-suite-.patch
+Patch7: ssm-0.4-test-008-Device-count-should-go-down-after-removing-.patch
+Patch8: ssm-0.4-misc-Fix-get_device_size-to-work-with-partitions-cor.patch
+Patch9: ssm-0.4-ssm-Suppress-backtrace-if-command-failed.patch
+Patch10: ssm-0.4-ssm-Add-SSM_PRINT_BACKTRACE-environment-variable.patch
+Patch11: ssm-0.4-misc-Use-wipefs-a-in-misc.wipefs-helper.patch
+Patch12: ssm-0.4-ssm-Fix-various-problems-found-by-pylint.patch
+Patch13: ssm-0.4-ssm-Remove-unnecessary-usr-bin-env-python.patch
+
+
BuildArch: noarch
BuildRequires: python-devel >= 2.6
Requires: python >= 2.6
@@ -41,6 +57,33 @@ technologies via a single unified interface.
%prep
%setup -q
+# Contains upstream patches
+# 39eb3058a02a2b482c4c5bdf0120b70605a6b23a..4210d7560585f2a241eaa5ebc364c98d49f565d2
+%patch1 -p1
+# misc: get_unit_size() accept different unit formats
+%patch2 -p1
+# ssm: btrfs backend: try to find the real device
+%patch3 -p1
+# ssm: force btrfs device add
+%patch4 -p1
+# tests: Fix they way we gather information about btrfs
+%patch5 -p1
+# tests: Make btrfs recognize devices from tests suite properly
+%patch6 -p1
+# test 008: Device count should go down after removing a device
+%patch7 -p1
+# misc: Fix get_device_size to work with partitions correctly
+%patch8 -p1
+# ssm: Suppress backtrace if command failed
+%patch9 -p1
+# ssm: Add SSM_PRINT_BACKTRACE environment variable
+%patch10 -p1
+# misc: Use wipefs -a in misc.wipefs() helper
+%patch11 -p1
+# ssm: Fix various problems found by pylint
+%patch12 -p1
+# ssm: Remove unnecessary /usr/bin/env python
+%patch13 -p1
%build
# nothing to build
@@ -50,6 +93,9 @@ technologies via a single unified interface.
rm -rf ${RPM_BUILD_ROOT}
%{__python} setup.py install --root=${RPM_BUILD_ROOT}
+%check
+make test
+
%files
%{_bindir}/ssm
@@ -60,6 +106,25 @@ rm -rf ${RPM_BUILD_ROOT}
%changelog
+* Mon Jan 20 2014 Lukas Czerner <lczerner at redhat.com> 0.4-4
+- Update to a new upstream release v0.4
+- Remove btrfs resize support
+- Unmount all btrfs subvolumes when removing a filesystem
+- Fix size argument parsing for create and snapshot command
+- Fix list output for some cases
+- Add support to create encrypted volumes with crypt backend
+- Add dry-run option
+- Fix removing volumes with crypt backend
+- Add raid1 and raid10 support for lvm backend
+- Allow to check btrfs volumes
+- Fix error handling when trying to resize btrfs subvolume
+- Fix ssm mount command so it detects directory properly
+- Suppress backtrace when a command fails
+- Fix ssm to recognize units in new btrfs output properly
+- Use correct sysfs file to get size for a partition
+- Fix ssm to be able add a device with signature to btrfs file system
+- Resognize btrfs devices from new btrfs output properly
+
* Fri Feb 15 2013 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 0.2-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
More information about the scm-commits
mailing list