Add fallback secure erase using shred

This prevents devices from failing cleaning if they don't support ATA
security erase. Instead, the device will be erased by overwriting using
GNU shred.

Change-Id: Ie4f88b97c8c34efe2397538662622abd0b963aea
This commit is contained in:
Jay Faulkner 2015-03-17 16:26:06 -07:00
parent ac4e9901d6
commit a17c824288
2 changed files with 52 additions and 14 deletions
ironic_python_agent

@ -357,11 +357,28 @@ class GenericHardwareManager(HardwareManager):
if self._ata_erase(block_device):
return
if self._shred_block_device(block_device):
return
msg = ('Unable to erase block device {0}: device is unsupported.'
).format(block_device.name)
LOG.error(msg)
raise errors.IncompatibleHardwareMethodError(msg)
def _shred_block_device(self, block_device):
"""Erase a block device using shred.
:param block_device: a BlockDevice object to be erased
:returns: True if the erase succeeds, False if it fails for any reason
"""
try:
utils.execute('shred', '--force', '--zero', '--verbose',
'--iterations', '1', block_device.name)
except errors.ProcessExecutionError:
return False
return True
def _get_ata_security_lines(self, block_device):
output = utils.execute('hdparm', '-I', block_device.name)[0]

@ -125,6 +125,19 @@ BLK_DEVICE_TEMPLATE = (
'KNAME="loop0" MODEL="" SIZE="109109248" ROTA="1" TYPE="loop"'
)
SHRED_OUTPUT = (
'shred: /dev/sda: pass 1/2 (random)...\n'
'shred: /dev/sda: pass 1/2 (random)...4.9GiB/29GiB 17%\n'
'shred: /dev/sda: pass 1/2 (random)...15GiB/29GiB 51%\n'
'shred: /dev/sda: pass 1/2 (random)...20GiB/29GiB 69%\n'
'shred: /dev/sda: pass 1/2 (random)...29GiB/29GiB 100%\n'
'shred: /dev/sda: pass 2/2 (000000)...\n'
'shred: /dev/sda: pass 2/2 (000000)...4.9GiB/29GiB 17%\n'
'shred: /dev/sda: pass 2/2 (000000)...15GiB/29GiB 51%\n'
'shred: /dev/sda: pass 2/2 (000000)...20GiB/29GiB 69%\n'
'shred: /dev/sda: pass 2/2 (000000)...29GiB/29GiB 100%\n'
)
class FakeHardwareManager(hardware.GenericHardwareManager):
def __init__(self, hardware_support):
@ -401,37 +414,45 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
])
@mock.patch.object(utils, 'execute')
def test_erase_block_device_ata_nosecurity(self, mocked_execute):
def test_erase_block_device_nosecurity_shred(self, mocked_execute):
hdparm_output = HDPARM_INFO_TEMPLATE.split('\nSecurity:')[0]
mocked_execute.side_effect = [
(hdparm_output, '')
(hdparm_output, ''),
(SHRED_OUTPUT, '')
]
block_device = hardware.BlockDevice('/dev/sda', 'big', 1073741824,
True)
self.assertRaises(errors.IncompatibleHardwareMethodError,
self.hardware.erase_block_device,
block_device)
self.hardware.erase_block_device(block_device)
mocked_execute.assert_has_calls([
mock.call('hdparm', '-I', '/dev/sda'),
mock.call('shred', '--force', '--zero', '--verbose',
'--iterations', '1', '/dev/sda')
])
@mock.patch.object(utils, 'execute')
def test_erase_block_device_ata_not_supported(self, mocked_execute):
def test_erase_block_device_notsupported_shred(self, mocked_execute):
hdparm_output = HDPARM_INFO_TEMPLATE % {
'supported': 'not\tsupported',
'enabled': 'not\tenabled',
'frozen': 'not\tfrozen',
'enhanced_erase': 'not\tsupported: enhanced erase',
'supported': 'not\tsupported',
'enabled': 'not\tenabled',
'frozen': 'not\tfrozen',
'enhanced_erase': 'not\tsupported: enhanced erase',
}
mocked_execute.side_effect = [
(hdparm_output, '')
(hdparm_output, ''),
(SHRED_OUTPUT, '')
]
block_device = hardware.BlockDevice('/dev/sda', 'big', 1073741824,
True)
self.assertRaises(errors.IncompatibleHardwareMethodError,
self.hardware.erase_block_device,
block_device)
self.hardware.erase_block_device(block_device)
mocked_execute.assert_has_calls([
mock.call('hdparm', '-I', '/dev/sda'),
mock.call('shred', '--force', '--zero', '--verbose',
'--iterations', '1', '/dev/sda')
])
@mock.patch.object(utils, 'execute')
def test_erase_block_device_ata_security_enabled(self, mocked_execute):