From 739f900a6857d506f363b75b7e586bdea3af0f2e Mon Sep 17 00:00:00 2001 From: Aparna Date: Tue, 7 Jun 2016 07:01:08 +0000 Subject: [PATCH] Sort values of raid_config for 'share_physical_disks' This commit adds the mechanism to sort the raid_config['logical_disks'] based on the number of physical disks when 'share_physical_disks' is True. Currenly it is sorted based on the 'size_gb' which doesn't allow to share two logical volumes, if the second volume needs more disks than the first volume. Change-Id: I6117ae686d7530542aa3a338c2e57d5658b4f739 --- proliantutils/hpssa/manager.py | 90 ++++++++++++++++ proliantutils/tests/hpssa/raid_constants.py | 71 ++++++++++++- proliantutils/tests/hpssa/test_manager.py | 111 +++++++++++++++++++- 3 files changed, 267 insertions(+), 5 deletions(-) diff --git a/proliantutils/hpssa/manager.py b/proliantutils/hpssa/manager.py index 6b80500..4f873d2 100644 --- a/proliantutils/hpssa/manager.py +++ b/proliantutils/hpssa/manager.py @@ -113,6 +113,11 @@ def create_configuration(raid_config): key=lambda x: x['size_gb']) + [x for x in raid_config['logical_disks'] if x['size_gb'] == "MAX"]) + if any(logical_disk['share_physical_disks'] + for logical_disk in logical_disks_sorted + if 'share_physical_disks' in logical_disk): + logical_disks_sorted = _sort_shared_logical_disks(logical_disks_sorted) + # We figure out the new disk created by recording the wwns # before and after the create, and then figuring out the # newly found wwn from it. @@ -168,6 +173,91 @@ def create_configuration(raid_config): return raid_config +def _sort_shared_logical_disks(logical_disks): + """Sort the logical disks based on the following conditions. + + When the share_physical_disks is True make sure we create the volume + which needs more disks first. This avoids the situation of insufficient + disks for some logical volume request. + + For example, + - two logical disk with number of disks - LD1(3), LD2(4) + - have 4 physical disks + In this case, if we consider LD1 first then LD2 will fail since not + enough disks available to create LD2. So follow a order for allocation + when share_physical_disks is True. + + Also RAID1 can share only when there is logical volume with only 2 disks. + So make sure we create RAID 1 first when share_physical_disks is True. + + And RAID 1+0 can share only when the logical volume with even number of + disks. + :param logical_disks: 'logical_disks' to be sorted for shared logical + disks. + :returns: the logical disks sorted based the above conditions. + """ + is_shared = (lambda x: True if ('share_physical_disks' in x and + x['share_physical_disks']) else False) + num_of_disks = (lambda x: x['number_of_physical_disks'] + if 'number_of_physical_disks' in x else + constants.RAID_LEVEL_MIN_DISKS[x['raid_level']]) + + # Separate logical disks based on share_physical_disks value. + # 'logical_disks_shared' when share_physical_disks is True and + # 'logical_disks_nonshared' when share_physical_disks is False + logical_disks_shared = [] + logical_disks_nonshared = [] + for x in logical_disks: + target = (logical_disks_shared if is_shared(x) + else logical_disks_nonshared) + target.append(x) + + # Separete logical disks with raid 1 from the 'logical_disks_shared' into + # 'logical_disks_shared_raid1' and remaining as + # 'logical_disks_shared_excl_raid1'. + logical_disks_shared_raid1 = [] + logical_disks_shared_excl_raid1 = [] + for x in logical_disks_shared: + target = (logical_disks_shared_raid1 if x['raid_level'] == '1' + else logical_disks_shared_excl_raid1) + target.append(x) + + # Sort the 'logical_disks_shared' in reverse order based on + # 'number_of_physical_disks' attribute, if provided, otherwise minimum + # disks required to create the logical volume. + logical_disks_shared = sorted(logical_disks_shared_excl_raid1, + reverse=True, + key=num_of_disks) + + # Move RAID 1+0 to first in 'logical_disks_shared' when number of physical + # disks needed to create logical volume cannot be shared with odd number of + # disks and disks higher than that of RAID 1+0. + check = True + for x in logical_disks_shared: + if x['raid_level'] == "1+0": + x_num = num_of_disks(x) + for y in logical_disks_shared: + if y['raid_level'] != "1+0": + y_num = num_of_disks(y) + if x_num < y_num: + check = (True if y_num % 2 == 0 else False) + if check: + break + if not check: + logical_disks_shared.remove(x) + logical_disks_shared.insert(0, x) + check = True + + # Final 'logical_disks_sorted' list should have non shared logical disks + # first, followed by shared logical disks with RAID 1, and finally by the + # shared logical disks sorted based on number of disks and RAID 1+0 + # condition. + logical_disks_sorted = (logical_disks_nonshared + + logical_disks_shared_raid1 + + logical_disks_shared) + return logical_disks_sorted + + def delete_configuration(): """Delete a RAID configuration on this server. diff --git a/proliantutils/tests/hpssa/raid_constants.py b/proliantutils/tests/hpssa/raid_constants.py index 1819009..a714afa 100644 --- a/proliantutils/tests/hpssa/raid_constants.py +++ b/proliantutils/tests/hpssa/raid_constants.py @@ -1497,7 +1497,7 @@ Available options are: ''' -HPSSA_NO_DRIVES_2_PHYSICAL_DISKS = ''' +HPSSA_NO_DRIVES_3_PHYSICAL_DISKS = ''' Smart Array P822 in Slot 2 Bus Interface: PCI Slot: 2 @@ -1575,6 +1575,28 @@ Smart Array P822 in Slot 2 Carrier Application Version: 11 Carrier Bootloader Version: 6 + physicaldrive 5I:1:3 + Port: 5I + Box: 1 + Bay: 1 + Status: OK + Drive Type: Unassigned Drive + Interface Type: SAS + Size: 700 GB + Native Block Size: 512 + Rotational Speed: 15000 + Firmware Revision: HPD6 + Serial Number: 6SL7G55D0000N4173JLT + Model: HP EF0600FARNA + Current Temperature (C): 35 + Maximum Temperature (C): 43 + PHY Count: 2 + PHY Transfer Rate: 6.0Gbps, Unknown + Drive Authentication Status: OK + Carrier Application Version: 11 + Carrier Bootloader Version: 6 + + SEP (Vendor ID PMCSIERA, Model SRCv24x6G) 380 Device Number: 380 Firmware Version: RevB @@ -1700,6 +1722,29 @@ Smart Array P822 in Slot 2 Carrier Application Version: 11 Carrier Bootloader Version: 6 + unassigned + + physicaldrive 5I:1:3 + Port: 5I + Box: 1 + Bay: 1 + Status: OK + Drive Type: Unassigned Drive + Interface Type: SAS + Size: 500 GB + Native Block Size: 512 + Rotational Speed: 15000 + Firmware Revision: HPD6 + Serial Number: 6SL7G55D0000N4173JLT + Model: HP EF0600FARNA + Current Temperature (C): 35 + Maximum Temperature (C): 43 + PHY Count: 2 + PHY Transfer Rate: 6.0Gbps, Unknown + Drive Authentication Status: OK + Carrier Application Version: 11 + Carrier Bootloader Version: 6 + ''' DRIVE_2_RAID_1_OKAY_TO_SHARE = ''' @@ -1850,6 +1895,30 @@ Smart Array P822 in Slot 2 Carrier Application Version: 11 Carrier Bootloader Version: 6 + unassigned + + physicaldrive 5I:1:3 + Port: 5I + Box: 1 + Bay: 2 + Status: OK + Drive Type: Data Drive + Interface Type: SAS + Size: 600 GB + Native Block Size: 512 + Rotational Speed: 15000 + Firmware Revision: HPD6 + Serial Number: 6SL7H2DM0000B41800Y0 + Model: HP EF0600FARNA + Current Temperature (C): 37 + Maximum Temperature (C): 44 + PHY Count: 2 + PHY Transfer Rate: 6.0Gbps, Unknown + Drive Authentication Status: OK + Carrier Application Version: 11 + Carrier Bootloader Version: 6 + + SEP (Vendor ID PMCSIERA, Model SRCv24x6G) 380 Device Number: 380 Firmware Version: RevB diff --git a/proliantutils/tests/hpssa/test_manager.py b/proliantutils/tests/hpssa/test_manager.py index 1f2661a..e888474 100644 --- a/proliantutils/tests/hpssa/test_manager.py +++ b/proliantutils/tests/hpssa/test_manager.py @@ -170,7 +170,7 @@ class ManagerTestCases(testtools.TestCase): @mock.patch.object(objects.Controller, 'execute_cmd') def test_create_configuration_share_physical_disks( self, controller_exec_cmd_mock, get_all_details_mock): - no_drives = raid_constants.HPSSA_NO_DRIVES_2_PHYSICAL_DISKS + no_drives = raid_constants.HPSSA_NO_DRIVES_3_PHYSICAL_DISKS one_drive = raid_constants.ONE_DRIVE_RAID_1 two_drives = raid_constants.TWO_DRIVES_50GB_RAID1 get_all_details_mock.side_effect = [no_drives, one_drive, two_drives] @@ -180,7 +180,8 @@ class ManagerTestCases(testtools.TestCase): (None, None)] raid_info = {'logical_disks': [{'size_gb': 50, 'share_physical_disks': True, - 'raid_level': '1', + 'number_of_physical_disks': 2, + 'raid_level': '0', 'disk_type': 'hdd'}, {'size_gb': 50, 'share_physical_disks': True, @@ -199,10 +200,44 @@ class ManagerTestCases(testtools.TestCase): 'create', 'type=logicaldrive', 'drives=5I:1:1,5I:1:2', 'raid=1', 'size=51200', process_input='y') controller_exec_cmd_mock.assert_any_call( - 'array', 'A', 'create', 'type=logicaldrive', 'raid=1', 'size=?', + 'array', 'A', 'create', 'type=logicaldrive', 'raid=0', 'size=?', dont_transform_to_hpssa_exception=True) controller_exec_cmd_mock.assert_any_call( - 'array', 'A', 'create', 'type=logicaldrive', 'raid=1', + 'array', 'A', 'create', 'type=logicaldrive', 'raid=0', + 'size=51200', process_input='y') + + @mock.patch.object(objects.Controller, 'execute_cmd') + def test_create_configuration_share_nonshare_physical_disks( + self, controller_exec_cmd_mock, get_all_details_mock): + no_drives = raid_constants.HPSSA_NO_DRIVES_3_PHYSICAL_DISKS + one_drive = raid_constants.ONE_DRIVE_RAID_1 + two_drives = raid_constants.TWO_DRIVES_50GB_RAID1 + get_all_details_mock.side_effect = [no_drives, one_drive, two_drives] + controller_exec_cmd_mock.side_effect = [ + (None, None), + (raid_constants.DRIVE_2_RAID_1_OKAY_TO_SHARE, None), + (None, None)] + raid_info = {'logical_disks': [{'size_gb': 50, + 'raid_level': '1', + 'disk_type': 'hdd'}, + {'size_gb': 50, + 'share_physical_disks': True, + 'raid_level': '0', + 'disk_type': 'hdd'}]} + raid_info = manager.create_configuration(raid_info) + ld1 = raid_info['logical_disks'][0] + ld2 = raid_info['logical_disks'][1] + self.assertEqual('Smart Array P822 in Slot 2', ld1['controller']) + self.assertEqual('Smart Array P822 in Slot 2', ld2['controller']) + self.assertEqual(sorted(['5I:1:1', '5I:1:2']), + sorted(ld1['physical_disks'])) + self.assertEqual(sorted(['5I:1:1', '5I:1:2']), + sorted(ld2['physical_disks'])) + controller_exec_cmd_mock.assert_any_call( + 'create', 'type=logicaldrive', 'drives=5I:1:1,5I:1:2', + 'raid=1', 'size=51200', process_input='y') + controller_exec_cmd_mock.assert_any_call( + 'create', 'type=logicaldrive', 'drives=5I:1:3', 'raid=0', 'size=51200', process_input='y') @mock.patch.object(objects.Controller, 'execute_cmd') @@ -234,6 +269,74 @@ class ManagerTestCases(testtools.TestCase): 'create', 'type=logicaldrive', 'drives=5I:1:3,5I:1:4,6I:1:5', 'raid=5', process_input='y') + def test__sort_shared_logical_disks(self, get_all_details_mock): + logical_disk_sorted_expected = [ + {'size_gb': 500, 'disk_type': 'hdd', 'raid_level': '1'}, + {'share_physical_disks': True, 'size_gb': 450, 'disk_type': 'hdd', + 'number_of_physical_disks': 6, 'raid_level': '0'}, + {'share_physical_disks': True, 'size_gb': 200, 'disk_type': 'hdd', + 'raid_level': '1+0'}, + {'share_physical_disks': True, 'size_gb': 200, 'disk_type': 'hdd', + 'raid_level': '0'}, + {'share_physical_disks': True, 'size_gb': 100, 'disk_type': 'hdd', + 'raid_level': '0'}] + logical_disks = [{'size_gb': 500, + 'disk_type': 'hdd', + 'raid_level': '1'}, + {'share_physical_disks': True, + 'size_gb': 450, + 'disk_type': 'hdd', + 'number_of_physical_disks': 6, + 'raid_level': '0'}, + {'share_physical_disks': True, + 'size_gb': 200, + 'disk_type': 'hdd', + 'raid_level': '1+0'}, + {'share_physical_disks': True, + 'size_gb': 200, + 'disk_type': 'hdd', + 'raid_level': '0'}, + {'share_physical_disks': True, + 'size_gb': 100, + 'disk_type': 'hdd', + 'raid_level': '0'}] + logical_disks_sorted = manager._sort_shared_logical_disks( + logical_disks) + self.assertEqual(logical_disks_sorted, logical_disk_sorted_expected) + + def test__sort_shared_logical_disks_raid10(self, get_all_details_mock): + logical_disk_sorted_expected = [ + {'size_gb': 600, 'disk_type': 'hdd', 'raid_level': '1'}, + {'share_physical_disks': False, 'size_gb': 400, 'disk_type': 'hdd', + 'raid_level': '1+0'}, + {'share_physical_disks': False, 'size_gb': 100, 'disk_type': 'hdd', + 'raid_level': '5'}, + {'share_physical_disks': True, 'size_gb': 550, 'disk_type': 'hdd', + 'raid_level': '1'}, + {'share_physical_disks': True, 'size_gb': 200, 'disk_type': 'hdd', + 'raid_level': '1+0'}, + {'share_physical_disks': True, 'size_gb': 450, 'disk_type': 'hdd', + 'number_of_physical_disks': 5, 'raid_level': '0'}, + {'share_physical_disks': True, 'size_gb': 300, 'disk_type': 'hdd', + 'raid_level': '5'}] + logical_disks = [ + {'size_gb': 600, 'disk_type': 'hdd', 'raid_level': '1'}, + {'share_physical_disks': True, 'size_gb': 550, 'disk_type': 'hdd', + 'raid_level': '1'}, + {'share_physical_disks': True, 'size_gb': 450, 'disk_type': 'hdd', + 'number_of_physical_disks': 5, 'raid_level': '0'}, + {'share_physical_disks': False, 'size_gb': 400, 'disk_type': 'hdd', + 'raid_level': '1+0'}, + {'share_physical_disks': True, 'size_gb': 300, 'disk_type': 'hdd', + 'raid_level': '5'}, + {'share_physical_disks': True, 'size_gb': 200, 'disk_type': 'hdd', + 'raid_level': '1+0'}, + {'share_physical_disks': False, 'size_gb': 100, 'disk_type': 'hdd', + 'raid_level': '5'}] + logical_disks_sorted = manager._sort_shared_logical_disks( + logical_disks) + self.assertEqual(logical_disks_sorted, logical_disk_sorted_expected) + @mock.patch.object(manager, 'get_configuration') @mock.patch.object(objects.Controller, 'execute_cmd') def test_delete_configuration(self, controller_exec_cmd_mock,