diff --git a/library/blivet.py b/library/blivet.py
index cb48e71..e1903f3 100644
--- a/library/blivet.py
+++ b/library/blivet.py
@@ -167,11 +167,16 @@ class BlivetBase(object):
raise NotImplementedError()
def _manage_one_encryption(self, device):
+ global safe_mode
ret = device
# Make sure to handle adjusting both existing stacks and future stacks.
if device == device.raw_device and self._spec_dict['encryption']:
# add luks
luks_name = "luks-%s" % device._name
+ if safe_mode and (device.original_format.type is not None or
+ device.original_format.name != get_format(None).name):
+ raise BlivetAnsibleError("cannot remove existing formatting on device '%s' in safe mode due to adding encryption" %
+ device._name)
if not device.format.exists:
fmt = device.format
else:
@@ -196,6 +201,10 @@ class BlivetBase(object):
ret = luks_device
elif device != device.raw_device and not self._spec_dict['encryption']:
# remove luks
+ if safe_mode and (device.original_format.type is not None or
+ device.original_format.name != get_format(None).name):
+ raise BlivetAnsibleError("cannot remove existing formatting on device '%s' in safe mode due to encryption removal" %
+ device._name)
if not device.format.exists:
fmt = device.format
else:
@@ -823,17 +832,21 @@ class BlivetPool(BlivetBase):
def manage(self):
""" Schedule actions to configure this pool according to the yaml input. """
+ global safe_mode
# look up the device
self._look_up_disks()
self._look_up_device()
# schedule destroy if appropriate, including member type change
- if not self.ultimately_present or self._member_management_is_destructive():
- if not self.ultimately_present:
- self._manage_volumes()
+ if not self.ultimately_present:
+ self._manage_volumes()
self._destroy()
- if not self.ultimately_present:
- return
+ return
+ elif self._member_management_is_destructive():
+ if safe_mode:
+ raise BlivetAnsibleError("cannot remove and recreate existing pool '%s' in safe mode" % self._pool['name'])
+ else:
+ self._destroy()
# schedule create if appropriate
self._create()
diff --git a/tests/create-test-file.yml b/tests/create-test-file.yml
new file mode 100644
index 0000000..d1091e2
--- /dev/null
+++ b/tests/create-test-file.yml
@@ -0,0 +1,13 @@
+# Create a file to be checked that it still exists and no data loss has occured.
+# To use:
+# - set testfile to a path under the mountpoint being tested
+# - include this file (create-test-file.yml) before executing the
+# operation to be tested
+# - execute the operation that could potentially result in a loss of
+# data in the filesystem where testfile is located
+# - include verify-data-preservation.yml
+
+- name: create a file
+ file:
+ path: "{{ testfile }}"
+ state: touch
diff --git a/tests/tests_luks.yml b/tests/tests_luks.yml
index f93efe5..f733714 100644
--- a/tests/tests_luks.yml
+++ b/tests/tests_luks.yml
@@ -2,8 +2,8 @@
- hosts: all
become: true
vars:
- storage_safe_mode: false
mount_location: '/opt/test1'
+ testfile: "{{ mount_location }}/quux"
volume_size: '5g'
tasks:
@@ -64,10 +64,47 @@
- include_tasks: verify-role-results.yml
+ - import_tasks: create-test-file.yml
+
+ - name: Test for correct handling of safe_mode
+ block:
+ - name: Remove the encryption layer
+ include_role:
+ name: storage
+ vars:
+ storage_volumes:
+ - name: foo
+ type: disk
+ disks: "{{ unused_disks }}"
+ mount_point: "{{ mount_location }}"
+ encryption: false
+ encryption_password: 'yabbadabbadoo'
+ - name: unreachable task
+ fail:
+ msg: UNREACH
+ rescue:
+ - name: Check that we failed in the role
+ assert:
+ that:
+ - ansible_failed_result.msg != 'UNREACH'
+ msg: "Role has not failed when it should have"
+
+ - name: Verify the output of the safe_mode test
+ assert:
+ that: "blivet_output.failed and
+ blivet_output.msg
+ |regex_search('cannot remove existing
+ formatting.*in safe mode due to encryption removal')
+ and not blivet_output.changed"
+ msg: "Unexpected behavior w/ existing filesystem in safe mode"
+
+ - import_tasks: verify-data-preservation.yml
+
- name: Remove the encryption layer
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_volumes:
- name: foo
type: disk
@@ -78,10 +115,47 @@
- include_tasks: verify-role-results.yml
+ - import_tasks: create-test-file.yml
+
+ - name: Test for correct handling of safe_mode
+ block:
+ - name: Add encryption to the volume
+ include_role:
+ name: storage
+ vars:
+ storage_volumes:
+ - name: foo
+ type: disk
+ disks: "{{ unused_disks }}"
+ mount_point: "{{ mount_location }}"
+ encryption: true
+ encryption_password: 'yabbadabbadoo'
+ - name: unreachable task
+ fail:
+ msg: UNREACH
+ rescue:
+ - name: Check that we failed in the role
+ assert:
+ that:
+ - ansible_failed_result.msg != 'UNREACH'
+ msg: "Role has not failed when it should have"
+
+ - name: Verify the output of the safe_mode test
+ assert:
+ that: "blivet_output.failed and
+ blivet_output.msg
+ |regex_search('cannot remove existing
+ formatting.*in safe mode due to adding encryption')
+ and not blivet_output.changed"
+ msg: "Unexpected behavior w/ existing filesystem in safe mode"
+
+ - import_tasks: verify-data-preservation.yml
+
- name: Add encryption to the volume
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_volumes:
- name: foo
type: disk
@@ -102,6 +176,7 @@
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: partition
@@ -135,6 +210,7 @@
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: partition
@@ -149,10 +225,51 @@
- include_tasks: verify-role-results.yml
+ - import_tasks: create-test-file.yml
+
+ - name: Test for correct handling of safe_mode
+ block:
+ - name: Remove the encryption layer
+ include_role:
+ name: storage
+ vars:
+ storage_pools:
+ - name: foo
+ type: partition
+ disks: "{{ unused_disks }}"
+ volumes:
+ - name: test1
+ type: partition
+ mount_point: "{{ mount_location }}"
+ size: 4g
+ encryption: false
+ encryption_password: 'yabbadabbadoo'
+ - name: unreachable task
+ fail:
+ msg: UNREACH
+ rescue:
+ - name: Check that we failed in the role
+ assert:
+ that:
+ - ansible_failed_result.msg != 'UNREACH'
+ msg: "Role has not failed when it should have"
+
+ - name: Verify the output of the safe_mode test
+ assert:
+ that: "blivet_output.failed and
+ blivet_output.msg
+ |regex_search('cannot remove existing
+ formatting.*in safe mode due to encryption removal')
+ and not blivet_output.changed"
+ msg: "Unexpected behavior w/ existing filesystem in safe mode"
+
+ - import_tasks: verify-data-preservation.yml
+
- name: Remove the encryption layer
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: partition
@@ -167,6 +284,48 @@
- include_tasks: verify-role-results.yml
+ - import_tasks: create-test-file.yml
+
+ - name: Test for correct handling of safe_mode
+ block:
+ - name: Add encryption to the volume
+ include_role:
+ name: storage
+ vars:
+ storage_pools:
+ - name: foo
+ type: partition
+ disks: "{{ unused_disks }}"
+ volumes:
+ - name: test1
+ type: partition
+ mount_point: "{{ mount_location }}"
+ size: 4g
+ encryption: true
+ encryption_password: 'yabbadabbadoo'
+
+ - name: unreachable task
+ fail:
+ msg: UNREACH
+
+ rescue:
+ - name: Check that we failed in the role
+ assert:
+ that:
+ - ansible_failed_result.msg != 'UNREACH'
+ msg: "Role has not failed when it should have"
+
+ - name: Verify the output of the safe_mode test
+ assert:
+ that: "blivet_output.failed and
+ blivet_output.msg
+ |regex_search('cannot remove existing
+ formatting.*in safe mode due to adding encryption')
+ and not blivet_output.changed"
+ msg: "Unexpected behavior w/ existing volume in safe mode"
+
+ - import_tasks: verify-data-preservation.yml
+
- name: Test key file handling
block:
- name: Create a key file
@@ -186,6 +345,7 @@
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: partition
@@ -216,6 +376,7 @@
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: lvm
@@ -248,6 +409,7 @@
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: lvm
@@ -264,10 +426,52 @@
- include_tasks: verify-role-results.yml
+ - import_tasks: create-test-file.yml
+
+ - name: Test for correct handling of safe_mode
+ block:
+ - name: Remove the encryption layer
+ include_role:
+ name: storage
+ vars:
+ storage_pools:
+ - name: foo
+ type: lvm
+ disks: "{{ unused_disks }}"
+ volumes:
+ - name: test1
+ mount_point: "{{ mount_location }}"
+ size: 4g
+ encryption: false
+ encryption_password: 'yabbadabbadoo'
+
+ - name: unreachable task
+ fail:
+ msg: UNREACH
+
+ rescue:
+ - name: Check that we failed in the role
+ assert:
+ that:
+ - ansible_failed_result.msg != 'UNREACH'
+ msg: "Role has not failed when it should have"
+
+ - name: Verify the output of the safe_mode test
+ assert:
+ that: "blivet_output.failed and
+ blivet_output.msg
+ |regex_search('cannot remove existing
+ formatting.*in safe mode due to encryption removal')
+ and not blivet_output.changed"
+ msg: "Unexpected behavior w/ existing volume in safe mode"
+
+ - import_tasks: verify-data-preservation.yml
+
- name: Remove the encryption layer
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: lvm
@@ -281,10 +485,52 @@
- include_tasks: verify-role-results.yml
+ - import_tasks: create-test-file.yml
+
+ - name: Test for correct handling of safe_mode
+ block:
+ - name: Add encryption to the volume
+ include_role:
+ name: storage
+ vars:
+ storage_pools:
+ - name: foo
+ type: lvm
+ disks: "{{ unused_disks }}"
+ volumes:
+ - name: test1
+ mount_point: "{{ mount_location }}"
+ size: 4g
+ encryption: true
+ encryption_password: 'yabbadabbadoo'
+
+ - name: unreachable task
+ fail:
+ msg: UNREACH
+
+ rescue:
+ - name: Check that we failed in the role
+ assert:
+ that:
+ - ansible_failed_result.msg != 'UNREACH'
+ msg: "Role has not failed when it should have"
+
+ - name: Verify the output of the safe_mode test
+ assert:
+ that: "blivet_output.failed and
+ blivet_output.msg
+ |regex_search('cannot remove existing
+ formatting.*in safe mode due to adding encryption')
+ and not blivet_output.changed"
+ msg: "Unexpected behavior w/ existing volume in safe mode"
+
+ - import_tasks: verify-data-preservation.yml
+
- name: Add encryption to the volume
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: lvm
diff --git a/tests/tests_luks_pool.yml b/tests/tests_luks_pool.yml
index b20b806..f44916f 100644
--- a/tests/tests_luks_pool.yml
+++ b/tests/tests_luks_pool.yml
@@ -2,9 +2,10 @@
- hosts: all
become: true
vars:
- storage_safe_mode: false
mount_location: '/opt/test1'
mount_location_2: '/opt/test2'
+ testfile: "{{ mount_location }}/quux"
+ testfile_location_2: "{{ mount_location_2 }}/quux"
volume_size: '5g'
tasks:
@@ -92,10 +93,50 @@
state: absent
changed_when: false
+ - import_tasks: create-test-file.yml
+
+ - name: Test for correct handling of safe_mode
+ block:
+ - name: Remove the encryption layer
+ include_role:
+ name: storage
+ vars:
+ storage_pools:
+ - name: foo
+ type: lvm
+ disks: "{{ unused_disks }}"
+ encryption: false
+ encryption_password: 'yabbadabbadoo'
+ volumes:
+ - name: test1
+ mount_point: "{{ mount_location }}"
+ size: 4g
+ - name: unreachable task
+ fail:
+ msg: UNREACH
+ rescue:
+ - name: Check that we failed in the role
+ assert:
+ that:
+ - ansible_failed_result.msg != 'UNREACH'
+ msg: "Role has not failed when it should have"
+
+ - name: Verify the output of the safe_mode test
+ assert:
+ that: "blivet_output.failed and
+ blivet_output.msg
+ |regex_search('cannot remove and recreate existing
+ pool.*in safe mode')
+ and not blivet_output.changed"
+ msg: "Unexpected behavior w/ existing pool in safe mode"
+
+ - import_tasks: verify-data-preservation.yml
+
- name: Remove the encryption layer
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: lvm
@@ -109,10 +150,53 @@
- include_tasks: verify-role-results.yml
- - name: Add encryption to the volume
+ - import_tasks: create-test-file.yml
+
+ - name: Test for correct handling of safe_mode
+ block:
+ - name: Add encryption to the pool
+ include_role:
+ name: storage
+ vars:
+ storage_pools:
+ - name: foo
+ type: lvm
+ disks: "{{ unused_disks }}"
+ encryption: true
+ encryption_password: 'yabbadabbadoo'
+ encryption_luks_version: luks1
+ encryption_key_size: 512
+ encryption_cipher: 'serpent-xts-plain64'
+ volumes:
+ - name: test1
+ mount_point: "{{ mount_location }}"
+ size: 4g
+ - name: unreachable task
+ fail:
+ msg: UNREACH
+ rescue:
+ - name: Check that we failed in the role
+ assert:
+ that:
+ - ansible_failed_result.msg != 'UNREACH'
+ msg: "Role has not failed when it should have"
+
+ - name: Verify the output of the safe_mode test
+ assert:
+ that: "blivet_output.failed and
+ blivet_output.msg
+ |regex_search('cannot remove and recreate existing
+ pool.*in safe mode')
+ and not blivet_output.changed"
+ msg: "Unexpected behavior w/ existing pool in safe mode"
+
+ - import_tasks: verify-data-preservation.yml
+
+ - name: Add encryption to the pool
include_role:
name: storage
vars:
+ storage_safe_mode: false
storage_pools:
- name: foo
type: lvm
@@ -129,6 +213,8 @@
- include_tasks: verify-role-results.yml
+ - import_tasks: create-test-file.yml
+
- name: Change the mountpoint, leaving encryption in place
include_role:
name: storage
@@ -144,6 +230,10 @@
mount_point: "{{ mount_location_2 }}"
size: 4g
+ - import_tasks: verify-data-preservation.yml
+ vars:
+ testfile: "{{ testfile_location_2 }}"
+
- include_tasks: verify-role-results.yml
- name: Clean up
diff --git a/tests/verify-data-preservation.yml b/tests/verify-data-preservation.yml
new file mode 100644
index 0000000..eed790f
--- /dev/null
+++ b/tests/verify-data-preservation.yml
@@ -0,0 +1,19 @@
+# Verify that a file still exists and no data loss has occured.
+# To use:
+# - set testfile to a path under the mountpoint being tested
+# - include create-test-file.yml before executing the operation to be
+# tested
+# - execute the operation that could potentially result in a loss of
+# data in the filesystem where testfile is located
+# - include this file (verify-data-preservation.yml)
+
+- name: stat the file
+ stat:
+ path: "{{ testfile }}"
+ register: stat_r
+
+- name: assert file presence
+ assert:
+ that:
+ stat_r.stat.isreg is defined and stat_r.stat.isreg
+ msg: "data lost!"