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):