mirror of
https://github.com/gryf/openstack.git
synced 2025-12-17 03:20:25 +01:00
system_installer software RAID setup implementation
This commit is contained in:
516
ironic-lib/0005-Software-RAID-setup-implementation.patch
Normal file
516
ironic-lib/0005-Software-RAID-setup-implementation.patch
Normal file
@@ -0,0 +1,516 @@
|
|||||||
|
From 9ea7095e9bc352089d44d8b79494aa9321378c31 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Grzegorz Grasza (xek)" <grzegorz.grasza@intel.com>
|
||||||
|
Date: Fri, 12 Jan 2018 10:48:48 +0100
|
||||||
|
Subject: [PATCH 05/11] Software RAID setup implementation
|
||||||
|
|
||||||
|
Co-Authored-By: Grzegorz Grasza <grzegorz.grasza@intel.com>
|
||||||
|
Co-Authored-By: Marta Mucek <marta.mucek@intel.com>
|
||||||
|
Co-Authored-By: Piotr Prokop <piotr.prokop@intel.com>
|
||||||
|
---
|
||||||
|
ironic_lib/system_installer/swraidsetup.py | 167 +++++++++++++
|
||||||
|
.../examples/swraid_outputs/mdstat_raid10.txt | 26 ++
|
||||||
|
ironic_lib/tests/test_swraidsetup.py | 270 +++++++++++++++++++++
|
||||||
|
3 files changed, 463 insertions(+)
|
||||||
|
create mode 100644 ironic_lib/tests/examples/swraid_outputs/mdstat_raid10.txt
|
||||||
|
create mode 100644 ironic_lib/tests/test_swraidsetup.py
|
||||||
|
|
||||||
|
diff --git a/ironic_lib/system_installer/swraidsetup.py b/ironic_lib/system_installer/swraidsetup.py
|
||||||
|
index 2965aaa..6a5ca2a 100644
|
||||||
|
--- a/ironic_lib/system_installer/swraidsetup.py
|
||||||
|
+++ b/ironic_lib/system_installer/swraidsetup.py
|
||||||
|
@@ -13,7 +13,17 @@
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
+import os
|
||||||
|
+
|
||||||
|
+from ironic_lib import utils
|
||||||
|
+from oslo_concurrency import processutils
|
||||||
|
+from oslo_log import log
|
||||||
|
+
|
||||||
|
from ironic_lib.system_installer.base import Setup
|
||||||
|
+from ironic_lib.system_installer import exceptions
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+LOG = log.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class SwRaidSetup(Setup):
|
||||||
|
@@ -21,9 +31,166 @@ class SwRaidSetup(Setup):
|
||||||
|
|
||||||
|
conf_key = 'swraid'
|
||||||
|
|
||||||
|
+ def validate_conf(self):
|
||||||
|
+ for raid in self.conf.values():
|
||||||
|
+ disks = len(raid['partitions'])
|
||||||
|
+ raid = raid['raidtype']
|
||||||
|
+ if (raid == 0 and (disks == 0 or (disks % 2))) \
|
||||||
|
+ or (raid == 1 and disks < 2) \
|
||||||
|
+ or (raid in (4, 5) and disks < 3) \
|
||||||
|
+ or (raid == 6 and disks < 4):
|
||||||
|
+ raise exceptions.ConfError(
|
||||||
|
+ "Wrong amount of disks for SW RAID {}.".format(raid))
|
||||||
|
+
|
||||||
|
def get_src_names(self):
|
||||||
|
raid_partitions = [r['partitions'] for r in self.conf.values()]
|
||||||
|
return sum(raid_partitions, [])
|
||||||
|
|
||||||
|
def get_dst_names(self):
|
||||||
|
return list(self.conf)
|
||||||
|
+
|
||||||
|
+ def is_raid10(self, name, partitions):
|
||||||
|
+ if self.conf[name]['raidtype'] != 1:
|
||||||
|
+ return False
|
||||||
|
+
|
||||||
|
+ for partition in partitions:
|
||||||
|
+ if self.conf.get(partition):
|
||||||
|
+ if self.conf[partition]['raidtype'] != 0:
|
||||||
|
+ return False
|
||||||
|
+
|
||||||
|
+ return True
|
||||||
|
+
|
||||||
|
+ def setup_disks(self, devices):
|
||||||
|
+ """Create RAID configuration on the bare metal"""
|
||||||
|
+ for name, conf in self.conf.items():
|
||||||
|
+ if name in devices or '/dev/' + name in devices:
|
||||||
|
+ continue # only overwrite disks not in devices
|
||||||
|
+
|
||||||
|
+ partitions = [devices.get(p, p) for p in conf['partitions']]
|
||||||
|
+ cmd = "yes | mdadm --create /dev/{name} " \
|
||||||
|
+ "--level={raidtype} --raid-devices={number} " \
|
||||||
|
+ "{partitions} --force".format(name=name,
|
||||||
|
+ raidtype=conf['raidtype'],
|
||||||
|
+ number=len(partitions),
|
||||||
|
+ partitions=' '.join(
|
||||||
|
+ partitions))
|
||||||
|
+ if self.is_raid10(name, partitions):
|
||||||
|
+ try:
|
||||||
|
+ out, err = utils.execute(cmd, shell=True)
|
||||||
|
+ except processutils.ProcessExecutionError as e:
|
||||||
|
+ # After successful creation this returns
|
||||||
|
+ # "mdadm: error opening /dev/md3:
|
||||||
|
+ # No such file or directory"
|
||||||
|
+ # with error code 2, which we can safely ignore
|
||||||
|
+ if e.exit_code != 2:
|
||||||
|
+ raise
|
||||||
|
+
|
||||||
|
+ else:
|
||||||
|
+ out, err = utils.execute(cmd, shell=True)
|
||||||
|
+
|
||||||
|
+ # making sure raid is created before moving any further
|
||||||
|
+ utils.execute('udevadm settle', shell=True)
|
||||||
|
+
|
||||||
|
+ LOG.debug("Debug mdadm create stdout: {}".format(out))
|
||||||
|
+ LOG.debug("Debug mdadm create stderr: {}".format(err))
|
||||||
|
+ devices[name] = '/dev/' + name
|
||||||
|
+ return devices
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def raid_partitions(self):
|
||||||
|
+ if not os.path.exists('/proc/mdstat'):
|
||||||
|
+ return {}
|
||||||
|
+
|
||||||
|
+ raid_part = dict()
|
||||||
|
+ with open('/proc/mdstat', 'r') as mdstat:
|
||||||
|
+ for line in mdstat.readlines():
|
||||||
|
+ if 'active raid' in line:
|
||||||
|
+ raid_part['/dev/{}'.format(line.split()[0])] =\
|
||||||
|
+ ['/dev/{}'.format(part.partition('[')[0])
|
||||||
|
+ for part in line.split()[4:]]
|
||||||
|
+
|
||||||
|
+ return raid_part
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def get_raid_10(self):
|
||||||
|
+ raids = self.raid_partitions()
|
||||||
|
+ raids_10 = []
|
||||||
|
+ for raid in raids:
|
||||||
|
+ for partition in raids[raid]:
|
||||||
|
+ if partition in raids.keys():
|
||||||
|
+ raids_10.append(raid)
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ # remove duplicates
|
||||||
|
+ return list(set(raids_10))
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def clean_disks(self, devices):
|
||||||
|
+ """Deletes all RAID configuration (from CERN hardware-manager)"""
|
||||||
|
+
|
||||||
|
+ devices_set = set(devices.values())
|
||||||
|
+ raids = self.raid_partitions()
|
||||||
|
+ raids_10 = self.get_raid_10()
|
||||||
|
+ # first we need to delete raids 10 if exists
|
||||||
|
+ for raid in raids_10:
|
||||||
|
+ # delete RAID
|
||||||
|
+ self.delete_raid(raid, devices_set)
|
||||||
|
+ # pop this RAID from raids
|
||||||
|
+ del raids[raid]
|
||||||
|
+
|
||||||
|
+ for device in raids:
|
||||||
|
+ if device in devices_set:
|
||||||
|
+ continue # free only disks not in devices
|
||||||
|
+ self.delete_raid(device, devices_set)
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def delete_raid(self, device, devices_set):
|
||||||
|
+ try:
|
||||||
|
+ detail, err = utils.execute(
|
||||||
|
+ "mdadm --detail {}".format(device), shell=True)
|
||||||
|
+ component_devices = [l.split()[-1] for l in detail.splitlines()
|
||||||
|
+ if 'active sync' in l]
|
||||||
|
+ LOG.debug("Component devices for {}: {}".format(
|
||||||
|
+ device, component_devices))
|
||||||
|
+
|
||||||
|
+ if err:
|
||||||
|
+ raise processutils.ProcessExecutionError(err)
|
||||||
|
+ except (processutils.ProcessExecutionError, OSError) as e:
|
||||||
|
+ raise exceptions.EnvError(
|
||||||
|
+ "Error getting details of RAID device {}. {}".format(
|
||||||
|
+ device, e))
|
||||||
|
+ if devices_set.intersection(set(component_devices)):
|
||||||
|
+ return
|
||||||
|
+ try:
|
||||||
|
+ # Positive output of the following goes into stderr, thus
|
||||||
|
+ # we don't want to check its content
|
||||||
|
+ utils.execute("mdadm --stop {}".format(device), shell=True)
|
||||||
|
+
|
||||||
|
+ except (processutils.ProcessExecutionError, OSError) as e:
|
||||||
|
+ raise exceptions.EnvError(
|
||||||
|
+ "Error stopping RAID device {}. {}".format(device, e))
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ utils.execute("mdadm --remove {}".format(device), shell=True)
|
||||||
|
+ except processutils.ProcessExecutionError as e:
|
||||||
|
+ # After successful stop this returns
|
||||||
|
+ # "mdadm: error opening /dev/md3: No such file or directory"
|
||||||
|
+ # with error code 1, which we can safely ignore
|
||||||
|
+ if e.exit_code != 1:
|
||||||
|
+ raise
|
||||||
|
+
|
||||||
|
+ for device in component_devices:
|
||||||
|
+ try:
|
||||||
|
+ _, err = utils.execute("mdadm --examine {}".format(device),
|
||||||
|
+ shell=True)
|
||||||
|
+ if "No md superblock detected" in err:
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ _, err = utils.execute("mdadm --zero-superblock {}".format(
|
||||||
|
+ device), shell=True)
|
||||||
|
+ if err:
|
||||||
|
+ raise processutils.ProcessExecutionError(err)
|
||||||
|
+ except (processutils.ProcessExecutionError, OSError) as e:
|
||||||
|
+ raise exceptions.EnvError(
|
||||||
|
+ "Error erasing superblock for device {}. {}".format(
|
||||||
|
+ device, e))
|
||||||
|
diff --git a/ironic_lib/tests/examples/swraid_outputs/mdstat_raid10.txt b/ironic_lib/tests/examples/swraid_outputs/mdstat_raid10.txt
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..34ef378
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/ironic_lib/tests/examples/swraid_outputs/mdstat_raid10.txt
|
||||||
|
@@ -0,0 +1,26 @@
|
||||||
|
+Personalities : [raid0] [raid1] [raid6] [raid5] [raid4]
|
||||||
|
+md3 : active raid0 sdb4[1] sda4[0]
|
||||||
|
+ 2319208448 blocks super 1.2 512k chunks
|
||||||
|
+
|
||||||
|
+md0 : active raid1 sdb1[1] sda1[0]
|
||||||
|
+ 3903488 blocks super 1.2 [2/2] [UU]
|
||||||
|
+ [=======>.............] resync = 37.1% (1451200/3903488) finish=0.8min speed=46812K/sec
|
||||||
|
+
|
||||||
|
+md1 : active raid5 sdd2[3] sdc2[2] sdb2[1] sda2[0]
|
||||||
|
+ 11710464 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/4] [UUUU]
|
||||||
|
+ resync=DELAYED
|
||||||
|
+
|
||||||
|
+md4 : active raid0 sdd4[1] sdc4[0]
|
||||||
|
+ 2319208448 blocks super 1.2 512k chunks
|
||||||
|
+
|
||||||
|
+md5 : active raid1 md4[1] md3[0]
|
||||||
|
+ 2319077376 blocks super 1.2 [2/2] [UU]
|
||||||
|
+ [>....................] resync = 0.2% (5174208/2319077376) finish=186.3min speed=206968K/sec
|
||||||
|
+ bitmap: 18/18 pages [72KB], 65536KB chunk
|
||||||
|
+
|
||||||
|
+md2 : active raid5 sdd3[3] sdc3[2] sdb3[1] sda3[0]
|
||||||
|
+ 11710464 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/4] [UUUU]
|
||||||
|
+ resync=DELAYED
|
||||||
|
+
|
||||||
|
+unused devices: <none>
|
||||||
|
+
|
||||||
|
diff --git a/ironic_lib/tests/test_swraidsetup.py b/ironic_lib/tests/test_swraidsetup.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..8b743c3
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/ironic_lib/tests/test_swraidsetup.py
|
||||||
|
@@ -0,0 +1,270 @@
|
||||||
|
+# Copyright (c) 2018 Intel Corporation
|
||||||
|
+#
|
||||||
|
+# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
+# you may not use this file except in compliance with the License.
|
||||||
|
+# You may obtain a copy of the License at
|
||||||
|
+#
|
||||||
|
+# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
+#
|
||||||
|
+# Unless required by applicable law or agreed to in writing, software
|
||||||
|
+# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
+# implied.
|
||||||
|
+# See the License for the specific language governing permissions and
|
||||||
|
+# limitations under the License.
|
||||||
|
+
|
||||||
|
+from collections import OrderedDict
|
||||||
|
+import unittest
|
||||||
|
+
|
||||||
|
+import mock
|
||||||
|
+from oslo_concurrency import processutils
|
||||||
|
+
|
||||||
|
+from ironic_lib.system_installer import exceptions
|
||||||
|
+from ironic_lib.system_installer import swraidsetup
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class SwRaidSetupTestCase(unittest.TestCase):
|
||||||
|
+
|
||||||
|
+ def test_validate_conf(self):
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup(
|
||||||
|
+ {'swraid': {'md0': {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['d0p1', 'd0p2']},
|
||||||
|
+ 'md1': {
|
||||||
|
+ 'raidtype': 2,
|
||||||
|
+ 'partitions': ['d1p1', 'd1p2']}}})
|
||||||
|
+ swraid_setup.validate_conf()
|
||||||
|
+
|
||||||
|
+ def test_validate_conf_error_raid4(self):
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup(
|
||||||
|
+ {'swraid': {'md0': {
|
||||||
|
+ 'raidtype': 4,
|
||||||
|
+ 'partitions': ['d0p1', 'd0p2']}}})
|
||||||
|
+ with self.assertRaises(exceptions.ConfError):
|
||||||
|
+ swraid_setup.validate_conf()
|
||||||
|
+
|
||||||
|
+ def test_validate_conf_error_raid6(self):
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup(
|
||||||
|
+ {'swraid': {'md1': {
|
||||||
|
+ 'raidtype': 6,
|
||||||
|
+ 'partitions': ['d1p1', 'd1p2', 'd1p2']}}})
|
||||||
|
+ with self.assertRaises(exceptions.ConfError):
|
||||||
|
+ swraid_setup.validate_conf()
|
||||||
|
+
|
||||||
|
+ def test_get_src_names(self):
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup(
|
||||||
|
+ {'swraid': {'md0': {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['d0p1', 'd0p2']},
|
||||||
|
+ 'md1': {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['d1p1', 'd1p2']}}})
|
||||||
|
+ self.assertEqual(set(swraid_setup.get_src_names()),
|
||||||
|
+ {'d0p1', 'd0p2', 'd1p1', 'd1p2'})
|
||||||
|
+
|
||||||
|
+ def test_get_dst_names(self):
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup(
|
||||||
|
+ {'swraid': {'md0': {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['d0p1', 'd0p2']},
|
||||||
|
+ 'md1': {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['d1p1', 'd1p2']}}})
|
||||||
|
+ self.assertEqual(set(swraid_setup.get_dst_names()),
|
||||||
|
+ {'md0', 'md1'})
|
||||||
|
+
|
||||||
|
+ def test_is_raid10_true(self):
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup(
|
||||||
|
+ {'swraid': {'md0': {
|
||||||
|
+ 'raidtype': 0,
|
||||||
|
+ 'partitions': ['d0p1', 'd0p2']},
|
||||||
|
+ 'md1': {
|
||||||
|
+ 'raidtype': 0,
|
||||||
|
+ 'partitions': ['d1p1', 'd1p2']},
|
||||||
|
+ 'md2': {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['md0', 'md1']}}})
|
||||||
|
+ self.assertTrue(swraid_setup.is_raid10('md2', ['md0', 'md1']))
|
||||||
|
+
|
||||||
|
+ def test_is_raid_false(self):
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup(
|
||||||
|
+ {'swraid': {'md0': {
|
||||||
|
+ 'raidtype': 0,
|
||||||
|
+ 'partitions': ['d0p1', 'd0p2']},
|
||||||
|
+ 'md1': {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['d1p1', 'd1p2']},
|
||||||
|
+ 'md2': {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['md0', 'md1']}}})
|
||||||
|
+ self.assertFalse(swraid_setup.is_raid10('md2', ['md0', 'md1']))
|
||||||
|
+
|
||||||
|
+ @mock.patch('ironic_lib.utils.execute')
|
||||||
|
+ def test_setup_disks_raise(self, exe):
|
||||||
|
+ exe.side_effect = processutils.ProcessExecutionError(None, None,
|
||||||
|
+ 3, None, 'foo')
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup({'swraid': OrderedDict([
|
||||||
|
+ ('md0', {'raidtype': 1,
|
||||||
|
+ 'partitions': ['d0p1', 'd0p2']}),
|
||||||
|
+ ('md1', {'raidtype': 2,
|
||||||
|
+ 'partitions': ['d1p1', 'd1p2']})])})
|
||||||
|
+ with self.assertRaises(processutils.ProcessExecutionError):
|
||||||
|
+ swraid_setup.setup_disks({})
|
||||||
|
+
|
||||||
|
+ def test_setup_disks(self):
|
||||||
|
+ with mock.patch('ironic_lib.utils.execute',
|
||||||
|
+ return_value=('', '')) as exe_mock:
|
||||||
|
+
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup({'swraid': OrderedDict([
|
||||||
|
+ ('md0', {
|
||||||
|
+ 'raidtype': 1,
|
||||||
|
+ 'partitions': ['d0p1', 'd0p2']}),
|
||||||
|
+ ('md1', {
|
||||||
|
+ 'raidtype': 2,
|
||||||
|
+ 'partitions': ['d1p1', 'd1p2']})])})
|
||||||
|
+ result = swraid_setup.setup_disks({'md0': '/dev/md0'})
|
||||||
|
+ self.assertDictEqual(
|
||||||
|
+ result, {'md0': '/dev/md0', 'md1': '/dev/md1'})
|
||||||
|
+ self.assertNotIn(mock.call(
|
||||||
|
+ 'yes | '
|
||||||
|
+ 'mdadm --create /dev/md0 --level=1 --raid-devices=2'
|
||||||
|
+ ' d0p1 d0p2 --force',
|
||||||
|
+ shell=True), exe_mock.mock_calls)
|
||||||
|
+ result = swraid_setup.setup_disks({})
|
||||||
|
+ self.assertDictEqual(
|
||||||
|
+ result, {'md0': '/dev/md0', 'md1': '/dev/md1'})
|
||||||
|
+ exe_mock.assert_any_call(
|
||||||
|
+ 'yes | '
|
||||||
|
+ 'mdadm --create /dev/md0 --level=1 --raid-devices=2'
|
||||||
|
+ ' d0p1 d0p2 --force',
|
||||||
|
+ shell=True)
|
||||||
|
+ exe_mock.assert_any_call(
|
||||||
|
+ 'yes | '
|
||||||
|
+ 'mdadm --create /dev/md1 --level=2 --raid-devices=2'
|
||||||
|
+ ' d1p1 d1p2 --force',
|
||||||
|
+ shell=True)
|
||||||
|
+
|
||||||
|
+ @mock.patch.object(swraidsetup.SwRaidSetup, 'get_raid_10')
|
||||||
|
+ @mock.patch.object(swraidsetup.SwRaidSetup, 'raid_partitions')
|
||||||
|
+ def test_clean_disks(self, raid, raid10):
|
||||||
|
+ raid.return_value = {'/dev/md0': ['/dev/md0']}
|
||||||
|
+ raid10.return_value = []
|
||||||
|
+ detail_output = """/dev/md0:
|
||||||
|
+ Version : 1.2
|
||||||
|
+ Creation Time : Mon Aug 8 21:19:06 2016
|
||||||
|
+ Raid Level : raid10
|
||||||
|
+ Array Size : 209584128 (199.88 GiB 214.61 GB)
|
||||||
|
+ Used Dev Size : 104792064 (99.94 GiB 107.31 GB)
|
||||||
|
+ Raid Devices : 4
|
||||||
|
+ Total Devices : 4
|
||||||
|
+ Persistence : Superblock is persistent
|
||||||
|
+
|
||||||
|
+ Update Time : Mon Aug 8 21:36:36 2016
|
||||||
|
+ State : active
|
||||||
|
+ Active Devices : 4
|
||||||
|
+Working Devices : 4
|
||||||
|
+ Failed Devices : 0
|
||||||
|
+ Spare Devices : 0
|
||||||
|
+
|
||||||
|
+ Layout : near=2
|
||||||
|
+ Chunk Size : 512K
|
||||||
|
+
|
||||||
|
+ Name : mdadmwrite:0 (local to host mdadmwrite)
|
||||||
|
+ UUID : 0dc2e687:1dfe70ac:d440b2ac:5828d61d
|
||||||
|
+ Events : 18
|
||||||
|
+
|
||||||
|
+ Number Major Minor RaidDevice State
|
||||||
|
+ 0 8 0 0 active sync set-A /dev/sda
|
||||||
|
+ 1 8 16 1 active sync set-B /dev/sdb
|
||||||
|
+ 2 8 32 2 active sync set-A /dev/sdc
|
||||||
|
+ 3 8 48 3 active sync set-B /dev/sdd
|
||||||
|
+"""
|
||||||
|
+
|
||||||
|
+ def execute_result(command, *args, **kwargs):
|
||||||
|
+ if '--detail' in command:
|
||||||
|
+ return detail_output, ''
|
||||||
|
+ return '', ''
|
||||||
|
+ with mock.patch('ironic_lib.utils.execute',
|
||||||
|
+ side_effect=execute_result) as exe_mock:
|
||||||
|
+
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup({'swraid': {}})
|
||||||
|
+ swraid_setup.clean_disks({'raid0': '/dev/md0'})
|
||||||
|
+ self.assertFalse(exe_mock.called)
|
||||||
|
+ swraid_setup.clean_disks({'raid0': '/dev/sda'})
|
||||||
|
+ exe_mock.assert_called_once_with('mdadm --detail /dev/md0',
|
||||||
|
+ shell=True)
|
||||||
|
+ self.assertNotIn(mock.call('mdadm --stop /dev/md0', shell=True),
|
||||||
|
+ exe_mock.mock_calls)
|
||||||
|
+ swraid_setup.clean_disks({})
|
||||||
|
+ exe_mock.assert_any_call('mdadm --stop /dev/md0', shell=True)
|
||||||
|
+ exe_mock.assert_any_call('mdadm --remove /dev/md0', shell=True)
|
||||||
|
+ exe_mock.assert_any_call(
|
||||||
|
+ 'mdadm --zero-superblock /dev/sda', shell=True)
|
||||||
|
+ exe_mock.assert_any_call(
|
||||||
|
+ 'mdadm --zero-superblock /dev/sdb', shell=True)
|
||||||
|
+ exe_mock.assert_any_call(
|
||||||
|
+ 'mdadm --zero-superblock /dev/sdc', shell=True)
|
||||||
|
+ exe_mock.assert_any_call(
|
||||||
|
+ 'mdadm --zero-superblock /dev/sdd', shell=True)
|
||||||
|
+
|
||||||
|
+ @mock.patch('os.path.exists')
|
||||||
|
+ @mock.patch('ironic_lib.system_installer.swraidsetup.open')
|
||||||
|
+ def test_raid_partitions(self, open_mock, exists):
|
||||||
|
+ exists.return_value = True
|
||||||
|
+ with open('ironic_lib/tests/examples/'
|
||||||
|
+ 'swraid_outputs/mdstat_raid10.txt') as f:
|
||||||
|
+ mdstat = f.read()
|
||||||
|
+
|
||||||
|
+ open_mock.return_value = mock.mock_open(read_data=mdstat).return_value
|
||||||
|
+ expected_output = {'/dev/md3': ['/dev/sdb4', '/dev/sda4'],
|
||||||
|
+ '/dev/md0': ['/dev/sdb1', '/dev/sda1'],
|
||||||
|
+ '/dev/md1': ['/dev/sdd2', '/dev/sdc2', '/dev/sdb2',
|
||||||
|
+ '/dev/sda2'],
|
||||||
|
+ '/dev/md4': ['/dev/sdd4', '/dev/sdc4'],
|
||||||
|
+ '/dev/md5': ['/dev/md4', '/dev/md3'],
|
||||||
|
+ '/dev/md2': ['/dev/sdd3', '/dev/sdc3', '/dev/sdb3',
|
||||||
|
+ '/dev/sda3']}
|
||||||
|
+
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup({'swraid': {}})
|
||||||
|
+ self.assertDictEqual(expected_output, swraid_setup.raid_partitions())
|
||||||
|
+
|
||||||
|
+ @mock.patch.object(swraidsetup.SwRaidSetup, 'raid_partitions')
|
||||||
|
+ def test_get_raid_10(self, partitions):
|
||||||
|
+ partitions.return_value = {'/dev/md3': ['/dev/sdb4', '/dev/sda4'],
|
||||||
|
+ '/dev/md0': ['/dev/sdb1', '/dev/sda1'],
|
||||||
|
+ '/dev/md1': ['/dev/sdd2', '/dev/sdc2',
|
||||||
|
+ '/dev/sdb2', '/dev/sda2'],
|
||||||
|
+ '/dev/md4': ['/dev/sdd4', '/dev/sdc4'],
|
||||||
|
+ '/dev/md5': ['/dev/md4', '/dev/md3'],
|
||||||
|
+ '/dev/md2': ['/dev/sdd3', '/dev/sdc3',
|
||||||
|
+ '/dev/sdb3', '/dev/sda3']}
|
||||||
|
+
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup({'swraid': {}})
|
||||||
|
+ self.assertListEqual(['/dev/md5'], swraid_setup.get_raid_10())
|
||||||
|
+
|
||||||
|
+ @mock.patch.object(swraidsetup.SwRaidSetup, 'raid_partitions')
|
||||||
|
+ def test_get_raid_10_empty(self, partitions):
|
||||||
|
+ partitions.return_value = {'/dev/md3': ['/dev/sdb4', '/dev/sda4'],
|
||||||
|
+ '/dev/md0': ['/dev/sdb1', '/dev/sda1'],
|
||||||
|
+ '/dev/md1': ['/dev/sdd2', '/dev/sdc2',
|
||||||
|
+ '/dev/sdb2', '/dev/sda2'],
|
||||||
|
+ '/dev/md4': ['/dev/sdd4', '/dev/sdc4'],
|
||||||
|
+ '/dev/md2': ['/dev/sdd3', '/dev/sdc3',
|
||||||
|
+ '/dev/sdb3', '/dev/sda3']}
|
||||||
|
+
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup({'swraid': {}})
|
||||||
|
+ self.assertListEqual([], swraid_setup.get_raid_10())
|
||||||
|
+
|
||||||
|
+ @mock.patch.object(swraidsetup.SwRaidSetup, 'raid_partitions')
|
||||||
|
+ @mock.patch.object(swraidsetup.SwRaidSetup, 'get_raid_10')
|
||||||
|
+ @mock.patch.object(swraidsetup.SwRaidSetup, 'delete_raid')
|
||||||
|
+ def test_clean_disks_raid10(self, delete, raid10, raids):
|
||||||
|
+ raid10.return_value = ['/dev/md5']
|
||||||
|
+ raids.return_value = {'/dev/md3': ['/dev/sdb4', '/dev/sda4'],
|
||||||
|
+ '/dev/md4': ['/dev/sdd4', '/dev/sdc4'],
|
||||||
|
+ '/dev/md5': ['/dev/md4', '/dev/md3']}
|
||||||
|
+ swraid_setup = swraidsetup.SwRaidSetup({'swraid': {}})
|
||||||
|
+ swraid_setup.clean_disks({})
|
||||||
|
+ delete.assert_any_call('/dev/md5', set([]))
|
||||||
|
+ delete.assert_any_call('/dev/md4', set([]))
|
||||||
|
+ delete.assert_any_call('/dev/md3', set([]))
|
||||||
|
--
|
||||||
|
2.14.1
|
||||||
|
|
||||||
Reference in New Issue
Block a user