From 1f9cf46a37ae6a50608c48effe5ee8be84f51a2a Mon Sep 17 00:00:00 2001 From: Ramakrishnan G Date: Mon, 25 May 2015 23:32:23 +0000 Subject: [PATCH] HPSSA: Support 'MAX' as size_gb for logical disks This commit enables support for using 'MAX' as a possible value for size_gb for logical disks. When 'MAX' is used for a logical disk, that logical disk is created last. The remaining amount of space available is given to that logical disk. Multiple logical disks with 'MAX' may be specified, but might not be an actual use-case. Change-Id: I1af332285251667a2d40b8854a4f3c36753a3322 --- proliantutils/hpssa/constants.py | 3 + proliantutils/hpssa/disk_allocator.py | 17 +- proliantutils/hpssa/manager.py | 12 +- proliantutils/hpssa/objects.py | 18 +- proliantutils/hpssa/raid_config_schema.json | 14 +- proliantutils/tests/hpssa/raid_constants.py | 305 ++++++++++++++++++ .../tests/hpssa/test_disk_allocator.py | 25 ++ proliantutils/tests/hpssa/test_manager.py | 29 ++ proliantutils/tests/hpssa/test_objects.py | 35 ++ 9 files changed, 444 insertions(+), 14 deletions(-) diff --git a/proliantutils/hpssa/constants.py b/proliantutils/hpssa/constants.py index 28f390c0..6a324fc9 100644 --- a/proliantutils/hpssa/constants.py +++ b/proliantutils/hpssa/constants.py @@ -56,6 +56,9 @@ RAID_LEVEL_MIN_DISKS = {RAID_0: 2, RAID_60: 8} +MINIMUM_DISK_SIZE = 1 + + def get_interface_type(ssa_interface): return INTERFACE_TYPE_MAP[ssa_interface] diff --git a/proliantutils/hpssa/disk_allocator.py b/proliantutils/hpssa/disk_allocator.py index e023adef..66db4604 100644 --- a/proliantutils/hpssa/disk_allocator.py +++ b/proliantutils/hpssa/disk_allocator.py @@ -69,11 +69,22 @@ def allocate_disks(logical_disk, server, raid_config): physical_drives = controller.unassigned_physical_drives physical_drives = _get_criteria_matching_disks(logical_disk, physical_drives) - physical_drives = [x for x in physical_drives - if x.size_gb >= size_gb] + + if size_gb != "MAX": + # If we want to allocate for a logical disk for which size_gb is + # mentioned, we take the smallest physical drives which is required + # to match the criteria. + reverse_sort = False + physical_drives = [x for x in physical_drives + if x.size_gb >= size_gb] + else: + # If we want to allocate for a logical disk for which size_gb is + # MAX, we take the largest physical drives available. + reverse_sort = True if len(physical_drives) >= number_of_physical_disks: - selected_drives = sorted(physical_drives, key=lambda x: x.size_gb) + selected_drives = sorted(physical_drives, key=lambda x: x.size_gb, + reverse=reverse_sort) selected_drive_ids = [x.id for x in selected_drives] logical_disk['controller'] = controller.id physical_disks = selected_drive_ids[:number_of_physical_disks] diff --git a/proliantutils/hpssa/manager.py b/proliantutils/hpssa/manager.py index 5c540545..6b805009 100644 --- a/proliantutils/hpssa/manager.py +++ b/proliantutils/hpssa/manager.py @@ -103,9 +103,15 @@ def create_configuration(raid_config): # In this case, for RAID1 configuration, if we were to consider # LD1 first and allocate PD3 and PD4 for it, then allocation would # fail. So follow a particular order for allocation. - logical_disks_sorted = sorted(raid_config['logical_disks'], - key=lambda x: int(x['size_gb']), - reverse=True) + # + # Also make sure we create the MAX logical_disks the last to make sure + # we allot only the remaining space available. + logical_disks_sorted = ( + sorted((x for x in raid_config['logical_disks'] + if x['size_gb'] != "MAX"), + reverse=True, + key=lambda x: x['size_gb']) + + [x for x in raid_config['logical_disks'] if x['size_gb'] == "MAX"]) # We figure out the new disk created by recording the wwns # before and after the create, and then figuring out the diff --git a/proliantutils/hpssa/objects.py b/proliantutils/hpssa/objects.py index 2852f2aa..6b053cac 100644 --- a/proliantutils/hpssa/objects.py +++ b/proliantutils/hpssa/objects.py @@ -378,14 +378,19 @@ class Controller(object): phy_drive_ids = ','.join(logical_drive_info['physical_disks']) cmd_args.append("drives=%s" % phy_drive_ids) - size_mb = logical_drive_info['size_gb'] * 1024 raid_level = logical_drive_info['raid_level'] - # For RAID levels (like 5+0 and 6+0), HPSSA names them differently. # Check if we have mapping stored, otherwise use the same. raid_level = constants.RAID_LEVEL_INPUT_TO_HPSSA_MAPPING.get( raid_level, raid_level) - cmd_args.extend(["raid=%s" % raid_level, "size=%s" % size_mb]) + cmd_args.append("raid=%s" % raid_level) + + # If size_gb is MAX, then don't pass size argument. HPSSA will + # automatically allocate the maximum # disks size possible to the + # logical disk. + if logical_drive_info['size_gb'] != "MAX": + size_mb = logical_drive_info['size_gb'] * 1024 + cmd_args.append("size=%s" % size_mb) self.execute_cmd(*cmd_args) @@ -443,6 +448,11 @@ class RaidArray(object): args = ("array", self.id, "create", "type=logicaldrive", "raid=%s" % raid_level, "size=?") + if logical_disk['size_gb'] != "MAX": + desired_disk_size = logical_disk['size_gb'] + else: + desired_disk_size = constants.MINIMUM_DISK_SIZE + try: stdout, stderr = self.parent.execute_cmd( *args, dont_transform_to_hpssa_exception=True) @@ -467,7 +477,7 @@ class RaidArray(object): return False max_size_gb = int(match.group(1)) / 1024 - return logical_disk['size_gb'] <= max_size_gb + return desired_disk_size <= max_size_gb class LogicalDrive(object): diff --git a/proliantutils/hpssa/raid_config_schema.json b/proliantutils/hpssa/raid_config_schema.json index e50c034f..5188a928 100644 --- a/proliantutils/hpssa/raid_config_schema.json +++ b/proliantutils/hpssa/raid_config_schema.json @@ -13,10 +13,16 @@ "description": "RAID level for the logical disk. Required." }, "size_gb": { - "type": "integer", - "minimum": 0, - "exclusiveMinimum": true, - "description": "Size (Integer) for the logical disk. Required." + "anyOf": [{ + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + { + "type": "string", + "enum": [ "MAX" ] + }], + "description": "Size in GiB (Integer) for the logical disk. Use 'MAX' as size_gb if this logical disk is supposed to use the rest of the space available. Required." }, "volume_name": { "type": "string", diff --git a/proliantutils/tests/hpssa/raid_constants.py b/proliantutils/tests/hpssa/raid_constants.py index f1b3db32..a85bb1da 100644 --- a/proliantutils/tests/hpssa/raid_constants.py +++ b/proliantutils/tests/hpssa/raid_constants.py @@ -1791,3 +1791,308 @@ Smart Array P822 in Slot 2 Vendor ID: PMCSIERA Model: SRCv24x6G ''' + + +NO_DRIVES_HPSSA_7_DISKS = ''' + +Smart Array P822 in Slot 3 + Bus Interface: PCI + Slot: 3 + Serial Number: PDVTF0BRH5T0KV + + unassigned + + physicaldrive 5I:1:1 + Port: 5I + Box: 1 + Bay: 1 + Status: OK + Interface Type: SAS + Size: 199 GB + Firmware Revision: HPD6 + Serial Number: 6SL7G4QV0000B41803GZ + Model: HP EF0600FARNA + + physicaldrive 5I:1:2 + Port: 5I + Box: 1 + Bay: 2 + Status: OK + Interface Type: SAS + Size: 200 GB + Firmware Revision: HPD6 + Serial Number: 6SL7HK0Y0000N419008G + Model: HP EF0600FARNA + + physicaldrive 5I:1:3 + Port: 5I + Box: 1 + Bay: 3 + Status: OK + Interface Type: SAS + Size: 600 GB + Firmware Revision: HPD6 + Serial Number: 6SL7H1L50000B4180V5Y + Model: HP EF0600FARNA + + physicaldrive 5I:1:4 + Port: 5I + Box: 1 + Bay: 4 + Status: OK + Interface Type: SAS + Size: 599 GB + Firmware Revision: HPD6 + Serial Number: 6SL7H1K30000B41800TT + Model: HP EF0600FARNA + + physicaldrive 6I:1:5 + Port: 6I + Box: 1 + Bay: 5 + Status: OK + Interface Type: SAS + Size: 598 GB + Firmware Revision: HPDB + Serial Number: 2AVUR97N + Model: HP EF0600FATFF + + physicaldrive 6I:1:6 + Port: 6I + Box: 1 + Bay: 6 + Status: OK + Interface Type: SAS + Size: 500 GB + Firmware Revision: HPDB + Serial Number: 2AVVJR1N + Model: HP EF0600FATFF + + physicaldrive 6I:1:7 + Port: 6I + Box: 1 + Bay: 7 + Status: OK + Interface Type: SAS + Size: 500 GB + Firmware Revision: HPDB + Serial Number: 2AVVENJN + Model: HP EF0600FATFF +''' + + +ONE_DRIVE_RAID_1_50_GB = ''' + +Smart Array P822 in Slot 3 + Slot: 3 + Serial Number: PDVTF0BRH5T0KV + + Array: A + Interface Type: SAS + Unused Space: 1042189 MB (91.1%) + Used Space: 100.0 GB (8.9%) + + Logical Drive: 1 + Size: 50.0 GB + Fault Tolerance: 1 + Status: OK + MultiDomain Status: OK + Unique Identifier: 600508B1001C861A72C774A7394AE2AC + Disk Name: /dev/sda + Logical Drive Label: 013400ABPDVTF0BRH5T0KV22C5 + LD Acceleration Method: Controller Cache + + physicaldrive 5I:1:1 + Port: 5I + Box: 1 + Bay: 1 + Status: OK + Interface Type: SAS + Size: 199 GB + Firmware Revision: HPD6 + Serial Number: 6SL7G4QV0000B41803GZ + Model: HP EF0600FARNA + + physicaldrive 5I:1:2 + Port: 5I + Box: 1 + Bay: 2 + Status: OK + Interface Type: SAS + Size: 200 GB + Firmware Revision: HPD6 + Serial Number: 6SL7HK0Y0000N419008G + Model: HP EF0600FARNA + + unassigned + + physicaldrive 5I:1:3 + Port: 5I + Box: 1 + Bay: 3 + Status: OK + Interface Type: SAS + Size: 600 GB + Firmware Revision: HPD6 + Serial Number: 6SL7H1L50000B4180V5Y + Model: HP EF0600FARNA + + physicaldrive 5I:1:4 + Port: 5I + Box: 1 + Bay: 4 + Status: OK + Interface Type: SAS + Size: 599 GB + Firmware Revision: HPD6 + Serial Number: 6SL7H1K30000B41800TT + Model: HP EF0600FARNA + + physicaldrive 6I:1:5 + Port: 6I + Box: 1 + Bay: 5 + Status: OK + Interface Type: SAS + Size: 598 GB + Firmware Revision: HPDB + Serial Number: 2AVUR97N + Model: HP EF0600FATFF + + physicaldrive 6I:1:6 + Port: 6I + Box: 1 + Bay: 6 + Status: OK + Interface Type: SAS + Size: 500 GB + Firmware Revision: HPDB + Serial Number: 2AVVJR1N + Model: HP EF0600FATFF + + physicaldrive 6I:1:7 + Port: 6I + Box: 1 + Bay: 7 + Status: OK + Interface Type: SAS + Size: 500 GB + Firmware Revision: HPDB + Serial Number: 2AVVENJN + Model: HP EF0600FATFF +''' + + +TWO_DRIVES_50GB_RAID1_MAXGB_RAID5 = ''' + +Smart Array P822 in Slot 3 + Slot: 3 + Serial Number: PDVTF0BRH5T0KV + + Array: A + Interface Type: SAS + Unused Space: 1042189 MB (91.1%) + Used Space: 100.0 GB (8.9%) + Status: OK + + Logical Drive: 1 + Size: 50.0 GB + Fault Tolerance: 1 + Status: OK + Unique Identifier: 600508B1001C861A72C774A7394AE2AC + Disk Name: /dev/sda + + physicaldrive 5I:1:1 + Port: 5I + Box: 1 + Bay: 1 + Status: OK + Interface Type: SAS + Size: 199 GB + Firmware Revision: HPD6 + Serial Number: 6SL7G4QV0000B41803GZ + Model: HP EF0600FARNA + + physicaldrive 5I:1:2 + Port: 5I + Box: 1 + Bay: 2 + Status: OK + Interface Type: SAS + Size: 200 GB + Firmware Revision: HPD6 + Serial Number: 6SL7HK0Y0000N419008G + Model: HP EF0600FARNA + + + Array: B + Interface Type: SAS + Unused Space: 0 MB (0.0%) + Used Space: 1.6 TB (100.0%) + Status: OK + MultiDomain Status: OK + Array Type: Data + HP SSD Smart Path: disable + + Logical Drive: 2 + Size: 1.1 TB + Fault Tolerance: 5 + Status: OK + Unique Identifier: 600508B1001CE9DE8551AEE29D5A72F7 + + physicaldrive 5I:1:3 + Port: 5I + Box: 1 + Bay: 3 + Status: OK + Interface Type: SAS + Size: 600 GB + Firmware Revision: HPD6 + Serial Number: 6SL7H1L50000B4180V5Y + Model: HP EF0600FARNA + + physicaldrive 5I:1:4 + Port: 5I + Box: 1 + Bay: 4 + Status: OK + Interface Type: SAS + Size: 599 GB + Firmware Revision: HPD6 + Serial Number: 6SL7H1K30000B41800TT + Model: HP EF0600FARNA + + physicaldrive 6I:1:5 + Port: 6I + Box: 1 + Bay: 5 + Status: OK + Interface Type: SAS + Size: 598 GB + Firmware Revision: HPDB + Serial Number: 2AVUR97N + Model: HP EF0600FATFF + + unassigned + + physicaldrive 6I:1:6 + Port: 6I + Box: 1 + Bay: 6 + Status: OK + Interface Type: SAS + Size: 500 GB + Firmware Revision: HPDB + Serial Number: 2AVVJR1N + + physicaldrive 6I:1:7 + Port: 6I + Box: 1 + Bay: 7 + Status: OK + Interface Type: SAS + Size: 500 GB + Firmware Revision: HPDB + Serial Number: 2AVVENJN + Model: HP EF0600FATFF +''' diff --git a/proliantutils/tests/hpssa/test_disk_allocator.py b/proliantutils/tests/hpssa/test_disk_allocator.py index 31a05ad0..cb304186 100644 --- a/proliantutils/tests/hpssa/test_disk_allocator.py +++ b/proliantutils/tests/hpssa/test_disk_allocator.py @@ -114,6 +114,31 @@ class DiskAllocatorTestCase(testtools.TestCase): self.assertEqual(sorted(['5I:1:3', '6I:1:7']), sorted(logical_disk['physical_disks'])) + def test_allocate_disks_max_okay(self, get_all_details_mock): + get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE + server = objects.Server() + + logical_disk = {'size_gb': 'MAX', + 'raid_level': '1', + 'disk_type': 'hdd', + 'interface_type': 'sas'} + + # Decrease size of three disks so that the remaining gets + # selected. + disk1 = server.controllers[0].get_physical_drive_by_id('5I:1:3') + disk2 = server.controllers[0].get_physical_drive_by_id('6I:1:7') + disk3 = server.controllers[0].get_physical_drive_by_id('5I:1:4') + disk1.size_gb = 300 + disk2.size_gb = 300 + disk3.size_gb = 300 + + raid_config = {'logical_disks': [logical_disk]} + disk_allocator.allocate_disks(logical_disk, server, raid_config) + self.assertEqual('Smart Array P822 in Slot 2', + logical_disk['controller']) + self.assertEqual(sorted(['6I:1:5', '6I:1:6']), + sorted(logical_disk['physical_disks'])) + def test_allocate_disks_disk_size_not_matching(self, get_all_details_mock): get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE diff --git a/proliantutils/tests/hpssa/test_manager.py b/proliantutils/tests/hpssa/test_manager.py index 97e65d58..dda3fe28 100644 --- a/proliantutils/tests/hpssa/test_manager.py +++ b/proliantutils/tests/hpssa/test_manager.py @@ -201,6 +201,35 @@ class ManagerTestCases(testtools.TestCase): 'array', 'A', 'create', 'type=logicaldrive', 'raid=1', 'size=51200') + @mock.patch.object(objects.Controller, 'execute_cmd') + def test_create_configuration_max_as_size_gb( + self, controller_exec_cmd_mock, get_all_details_mock): + no_drives = raid_constants.NO_DRIVES_HPSSA_7_DISKS + one_drive = raid_constants.ONE_DRIVE_RAID_1_50_GB + two_drives = raid_constants.TWO_DRIVES_50GB_RAID1_MAXGB_RAID5 + get_all_details_mock.side_effect = [no_drives, one_drive, two_drives] + raid_info = {'logical_disks': [{'size_gb': 50, + 'raid_level': '1', + 'disk_type': 'hdd'}, + {'size_gb': 'MAX', + 'raid_level': '5', + '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 3', ld1['controller']) + self.assertEqual('Smart Array P822 in Slot 3', ld2['controller']) + self.assertEqual(sorted(['5I:1:1', '5I:1:2']), + sorted(ld1['physical_disks'])) + self.assertEqual(sorted(['5I:1:3', '5I:1:4', '6I:1:5']), + 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') + controller_exec_cmd_mock.assert_any_call( + 'create', 'type=logicaldrive', 'drives=5I:1:3,5I:1:4,6I:1:5', + 'raid=5') + @mock.patch.object(manager, 'get_configuration') @mock.patch.object(objects.Controller, 'execute_cmd') def test_delete_configuration(self, controller_exec_cmd_mock, diff --git a/proliantutils/tests/hpssa/test_objects.py b/proliantutils/tests/hpssa/test_objects.py index 0bc271d4..a4180e28 100644 --- a/proliantutils/tests/hpssa/test_objects.py +++ b/proliantutils/tests/hpssa/test_objects.py @@ -232,6 +232,28 @@ class ControllerTest(testtools.TestCase): "raid=1", "size=51200") + @mock.patch.object(objects.Controller, 'execute_cmd') + def test_create_logical_drive_max_size_gb(self, execute_mock, + get_all_details_mock): + + get_all_details_mock.return_value = raid_constants.HPSSA_NO_DRIVES + + server = objects.Server() + controller = server.controllers[0] + + logical_drive_info = {'size_gb': 'MAX', + 'raid_level': '1', + 'controller': 'Smart Array P822 in Slot 2', + 'physical_disks': ['5I:1:1', + '5I:1:2', + '5I:1:3']} + + controller.create_logical_drive(logical_drive_info) + execute_mock.assert_called_once_with("create", + "type=logicaldrive", + "drives=5I:1:1,5I:1:2,5I:1:3", + "raid=1") + @mock.patch.object(objects.Controller, 'execute_cmd') def test_create_logical_drive_with_raid_array(self, execute_mock, get_all_details_mock): @@ -363,6 +385,19 @@ class ArrayTest(testtools.TestCase): logical_disk) self.assertTrue(ret_val) + @mock.patch.object(processutils, 'execute') + def test_can_accomodate_max_size_gb_okay(self, execute_mock, + get_all_details_mock): + current_config = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1 + get_all_details_mock.return_value = current_config + execute_mock.return_value = ( + raid_constants.ARRAY_ACCOMODATE_LOGICAL_DISK, None) + logical_disk = {'size_gb': 'MAX', 'raid_level': '5'} + server = objects.Server() + ret_val = server.controllers[0].raid_arrays[0].can_accomodate( + logical_disk) + self.assertTrue(ret_val) + @mock.patch.object(processutils, 'execute') def test_can_accomodate_not_enough_space(self, execute_mock, get_all_details_mock):