HPSSA: Add disk allocator

This commit adds the disk allocator which allocates physical
disks to create requests (if physical_disks were not provided
in the input). This commit modifies the size of disks in the
test input data to facilitate writing easy test casese for
disk allocator.

Implements: blueprint hpssa-support
Change-Id: I7b48aa68cb9305596ab772a410d2c497313935b6
This commit is contained in:
Ramakrishnan G
2015-02-21 11:20:20 +00:00
parent cf1dc1f0d8
commit 905a11f94c
8 changed files with 375 additions and 70 deletions

View File

@@ -12,22 +12,23 @@
# License for the specific language governing permissions and limitations
# under the License.
INTERFACE_TYPE_SAS = 'SAS'
INTERFACE_TYPE_SCSI = 'SCSI'
INTERFACE_TYPE_SATA = 'SATA'
INTERFACE_TYPE_SAS = 'sas'
INTERFACE_TYPE_SCSI = 'scsi'
INTERFACE_TYPE_SATA = 'sata'
DISK_TYPE_HDD = 'HDD'
DISK_TYPE_SSD = 'SSD'
DISK_TYPE_HDD = 'hdd'
DISK_TYPE_SSD = 'ssd'
RAID_0 = '0'
RAID_1 = '1'
RAID_1_ADM = '1ADM'
RAID_10 = '10'
RAID_10_ADM = '10ADM'
RAID_10 = '1+0'
RAID_5 = '5'
RAID_6 = '6'
RAID_50 = '50'
RAID_60 = '60'
RAID_50 = '5+0'
RAID_60 = '6+0'
# Below are not supported in Ironic now.
RAID_1_ADM = '1ADM'
RAID_10_ADM = '10ADM'
INTERFACE_TYPE_MAP = {'SCSI': INTERFACE_TYPE_SCSI,
@@ -42,6 +43,14 @@ DISK_TYPE_MAP = {'SCSI': DISK_TYPE_HDD,
'SATASSD': DISK_TYPE_SSD,
'SASSSD': DISK_TYPE_SSD}
RAID_LEVEL_MIN_DISKS = {RAID_0: 2,
RAID_1: 2,
RAID_1_ADM: 3,
RAID_5: 3,
RAID_6: 4,
RAID_10: 4,
RAID_50: 6}
def get_interface_type(ssa_interface):
return INTERFACE_TYPE_MAP[ssa_interface]

View File

@@ -0,0 +1,88 @@
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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 proliantutils import exception
from proliantutils.hpssa import constants
FILTER_CRITERIA = ['disk_type', 'interface_type', 'model', 'firmware']
def _get_criteria_matching_disks(logical_disk, physical_drives):
"""Finds the physical drives matching the criteria of logical disk.
This method finds the physical drives matching the criteria
of the logical disk passed.
:param logical_disk: The logical disk dictionary from raid config
:param physical_drives: The physical drives to consider.
:returns: A list of physical drives which match the criteria
"""
matching_physical_drives = []
criteria_to_consider = [x for x in FILTER_CRITERIA
if x in logical_disk]
for physical_drive in physical_drives:
for criteria in criteria_to_consider:
logical_drive_value = logical_disk.get(criteria)
physical_drive_value = getattr(physical_drive, criteria)
if logical_drive_value != physical_drive_value:
break
else:
matching_physical_drives.append(physical_drive)
return matching_physical_drives
def allocate_disks(logical_disk, server):
"""Allocate physical disks to a logical disk.
This method allocated physical disks to a logical
disk based on the current state of the server and
criteria mentioned in the logical disk.
:param logical_disk: a dictionary of a logical disk
from the RAID configuration input to the module.
:param server: An objects.Server object
:raises: PhysicalDisksNotFoundError, if cannot find
physical disks for the request.
"""
size_gb = logical_disk['size_gb']
raid_level = logical_disk['raid_level']
number_of_physical_disks = logical_disk.get(
'number_of_physical_disks', constants.RAID_LEVEL_MIN_DISKS[raid_level])
share_physical_disks = logical_disk.get('share_physical_disks', False)
for controller in server.controllers:
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 len(physical_drives) >= number_of_physical_disks:
selected_drives = sorted(physical_drives, key=lambda x: x.size_gb)
selected_drive_ids = [x.id for x in selected_drives]
logical_disk['controller'] = controller.id
physical_disks = selected_drive_ids[:number_of_physical_disks]
logical_disk['physical_disks'] = physical_disks
break
if not share_physical_disks:
# TODO(rameshg87): When this logical drives can share disks
# with other arrays, figure out free space in other arrays
# and then consider which array to use.
pass
else:
raise exception.PhysicalDisksNotFoundError(size_gb=size_gb,
raid_level=raid_level)

View File

@@ -19,17 +19,14 @@ import jsonschema
from jsonschema import exceptions as json_schema_exc
from proliantutils import exception
from proliantutils.hpssa import constants
from proliantutils.hpssa import disk_allocator
from proliantutils.hpssa import objects
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
RAID_CONFIG_SCHEMA = os.path.join(CURRENT_DIR, "raid_config_schema.json")
def _find_physical_disks(logical_disk, server):
# To be implemented
pass
def validate(raid_config):
"""Validates the RAID configuration provided.
@@ -46,6 +43,27 @@ def validate(raid_config):
except json_schema_exc.ValidationError as e:
raise exception.InvalidInputError(e.message)
for logical_disk in raid_config['logical_disks']:
# If user has provided 'number_of_physical_disks' or
# 'physical_disks', validate that they have mentioned at least
# minimum number of physical disks required for that RAID level.
raid_level = logical_disk['raid_level']
min_disks_reqd = constants.RAID_LEVEL_MIN_DISKS[raid_level]
no_of_disks_specified = None
if 'number_of_physical_disks' in logical_disk:
no_of_disks_specified = logical_disk['number_of_physical_disks']
elif 'physical_disks' in logical_disk:
no_of_disks_specified = len(logical_disk['physical_disks'])
if (no_of_disks_specified and
no_of_disks_specified < min_disks_reqd):
msg = ("RAID level %(raid_level)s requires at least %(number) "
"disks." % {'raid_level': raid_level,
'number': min_disks_reqd})
raise exception.InvalidInputError(msg)
def create_configuration(raid_config):
"""Create a RAID configuration on this server.
@@ -89,14 +107,7 @@ def create_configuration(raid_config):
for logical_disk in logical_disks_sorted:
if 'physical_disks' not in logical_disk:
# TODO(rameshg87): hpssa module should be capable of finding
# the suitable controller and physical disks if it not provided
# by using hints. This is a supported use-case, but not implemented
# as of now. Will be implemented soon.
# _find_physical_disks(logical_disk, server)
msg = ("Mentioning logical_disks without 'controller' and "
"'physical_disks' is not supported as of now.")
raise exception.InvalidInputError(reason=msg)
disk_allocator.allocate_disks(logical_disk, server)
controller_id = logical_disk['controller']
@@ -173,23 +184,7 @@ def get_configuration():
raid_config['logical_disks'] = []
for logical_drive in logical_drives:
logical_drive_info = {}
logical_drive_info['size_gb'] = logical_drive.size_gb
logical_drive_info['raid_level'] = logical_drive.raid_level
array = logical_drive.parent
controller = array.parent
logical_drive_info['controller'] = controller.id
physical_drive_ids = map(lambda x: x.id, array.physical_drives)
logical_drive_info['physical_disks'] = physical_drive_ids
vol_name = logical_drive.get_property('Logical Drive Label')
logical_drive_info['volume_name'] = vol_name
wwn = logical_drive.get_property('Unique Identifier')
logical_drive_info['root_device_hint'] = {'wwn': wwn}
raid_config['logical_disks'].append(logical_drive_info)
logical_drive_dict = logical_drive.get_logical_drive_dict()
raid_config['logical_disks'].append(logical_drive_dict)
return raid_config

View File

@@ -17,7 +17,7 @@ import time
from oslo.concurrency import processutils
from proliantutils import exception
from proliantutils.hpssa import types
from proliantutils.hpssa import constants
def _get_indentation(string):
@@ -418,5 +418,7 @@ class PhysicalDrive:
self.size_gb = int(float(self.properties['Size'].rstrip(' GB')))
ssa_interface = self.properties['Interface Type']
self.interface_type = types.get_interface_type(ssa_interface)
self.disk_type = types.get_disk_type(ssa_interface)
self.interface_type = constants.get_interface_type(ssa_interface)
self.disk_type = constants.get_disk_type(ssa_interface)
self.model = self.properties.get('Model')
self.firmware = self.properties.get('Firmware Revision')

View File

@@ -55,7 +55,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 500 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -97,7 +97,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -118,7 +118,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -139,7 +139,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -285,7 +285,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 500 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -306,7 +306,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 500 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -330,7 +330,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -351,7 +351,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -372,7 +372,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -516,7 +516,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -537,7 +537,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -558,7 +558,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -582,7 +582,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 500 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -603,7 +603,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Unassigned Drive
Interface Type: SAS
Size: 600 GB
Size: 500 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -748,7 +748,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -769,7 +769,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -790,7 +790,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 400 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -844,7 +844,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 500 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6
@@ -865,7 +865,7 @@ Smart Array P822 in Slot 2
Status: OK
Drive Type: Data Drive
Interface Type: SAS
Size: 600 GB
Size: 500 GB
Native Block Size: 512
Rotational Speed: 15000
Firmware Revision: HPD6

View File

@@ -0,0 +1,145 @@
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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.
import mock
import testtools
from proliantutils import exception
from proliantutils.hpssa import disk_allocator
from proliantutils.hpssa import objects
from proliantutils.tests.hpssa import raid_constants
@mock.patch.object(objects.Server, '_get_all_details')
class DiskAllocatorTestCase(testtools.TestCase):
def test__get_criteria_matching_disks_all_criterias(self,
get_all_details_mock):
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
server = objects.Server()
physical_drives = server.controllers[0].unassigned_physical_drives
logical_disk = {'size_gb': 100,
'raid_level': '1',
'disk_type': 'hdd',
'interface_type': 'sas',
'model': 'HP EF0600FARNA',
'firmware': 'HPD6'}
ret_physical_drives = disk_allocator._get_criteria_matching_disks(
logical_disk, physical_drives)
self.assertEqual(ret_physical_drives, physical_drives)
def test__get_criteria_matching_disks_not_all_criterias(
self, get_all_details_mock):
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
server = objects.Server()
physical_drives = server.controllers[0].unassigned_physical_drives
logical_disk = {'size_gb': 100,
'raid_level': '1',
'disk_type': 'hdd',
'interface_type': 'sas',
'firmware': 'HPD6'}
ret_physical_drives = disk_allocator._get_criteria_matching_disks(
logical_disk, physical_drives)
self.assertEqual(ret_physical_drives, physical_drives)
def test__get_criteria_matching_disks_some_disks_dont_match(
self, get_all_details_mock):
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
server = objects.Server()
physical_drives = server.controllers[0].unassigned_physical_drives
logical_disk = {'size_gb': 100,
'raid_level': '1',
'disk_type': 'hdd',
'interface_type': 'sas',
'firmware': 'HPD6'}
physical_drives[0].disk_type = 'ssd'
physical_drives[1].firmware = 'HPD7'
ret_physical_drives = disk_allocator._get_criteria_matching_disks(
logical_disk, physical_drives)
exp_physical_drives = physical_drives[2:]
self.assertEqual(exp_physical_drives, ret_physical_drives)
def test__get_criteria_matching_disks_no_disks_match(
self, get_all_details_mock):
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
server = objects.Server()
physical_drives = server.controllers[0].unassigned_physical_drives
logical_disk = {'size_gb': 100,
'raid_level': '1',
'disk_type': 'ssdd',
'interface_type': 'sas',
'firmware': 'HPD6'}
ret_physical_drives = disk_allocator._get_criteria_matching_disks(
logical_disk, physical_drives)
self.assertFalse(ret_physical_drives)
def test_allocate_disks_okay(self, get_all_details_mock):
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
server = objects.Server()
logical_disk = {'size_gb': 100,
'raid_level': '1',
'disk_type': 'hdd',
'interface_type': 'sas'}
# Decrease size of two disks so that they get 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')
disk1.size_gb = 300
disk2.size_gb = 300
disk_allocator.allocate_disks(logical_disk, server)
self.assertEqual('Smart Array P822 in Slot 2',
logical_disk['controller'])
self.assertEqual(sorted(['5I:1:3', '6I:1:7']),
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
server = objects.Server()
logical_disk = {'size_gb': 700,
'raid_level': '1',
'disk_type': 'hdd',
'interface_type': 'sas'}
exc = self.assertRaises(exception.PhysicalDisksNotFoundError,
disk_allocator.allocate_disks,
logical_disk, server)
self.assertIn("of size 700 GB and raid level 1", str(exc))
def test_allocate_disks_disk_not_enough_disks(self,
get_all_details_mock):
get_all_details_mock.return_value = raid_constants.HPSSA_ONE_DRIVE
server = objects.Server()
physical_drives = server.controllers[0].unassigned_physical_drives
physical_drives = physical_drives[:2]
server.controllers[0].unassigned_physical_drives = physical_drives
logical_disk = {'size_gb': 600,
'raid_level': '5',
'disk_type': 'hdd',
'interface_type': 'sas'}
exc = self.assertRaises(exception.PhysicalDisksNotFoundError,
disk_allocator.allocate_disks,
logical_disk, server)
self.assertIn("of size 600 GB and raid level 5", str(exc))

View File

@@ -101,6 +101,57 @@ class ManagerTestCases(testtools.TestCase):
manager.create_configuration,
raid_info)
@mock.patch.object(objects.Controller, 'execute_cmd')
def test_create_configuration_without_disk_input_succeeds(
self, controller_exec_cmd_mock, get_all_details_mock):
no_drives = raid_constants.HPSSA_NO_DRIVES
one_drive = raid_constants.HPSSA_ONE_DRIVE_100GB_RAID_5
two_drives = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
get_all_details_mock.side_effect = [no_drives, one_drive, two_drives]
raid_info = {'logical_disks': [{'size_gb': 50,
'raid_level': '1'},
{'size_gb': 100,
'raid_level': '5'}]}
current_config = manager.create_configuration(raid_info)
controller_exec_cmd_mock.assert_any_call("create",
"type=logicaldrive",
mock.ANY,
"raid=5",
"size=%d" % (100*1024))
# Verify that we created the 50GB disk the last.
controller_exec_cmd_mock.assert_called_with("create",
"type=logicaldrive",
mock.ANY,
"raid=1",
"size=%d" % (50*1024))
ld1_ret = [x for x in current_config['logical_disks']
if x['raid_level'] == '1'][0]
ld2_ret = [x for x in current_config['logical_disks']
if x['raid_level'] == '5'][0]
self.assertEqual('600508B1001CC42CDF101F06E5563967',
ld2_ret['root_device_hint']['wwn'])
self.assertEqual('600508B1001CE1E18302A8702C6AB008',
ld1_ret['root_device_hint']['wwn'])
@mock.patch.object(objects.Controller, 'execute_cmd')
def test_create_configuration_without_disk_input_fails_on_disk_type(
self, controller_exec_cmd_mock, get_all_details_mock):
no_drives = raid_constants.HPSSA_NO_DRIVES
one_drive = raid_constants.HPSSA_ONE_DRIVE_100GB_RAID_5
two_drives = raid_constants.HPSSA_TWO_DRIVES_100GB_RAID5_50GB_RAID1
get_all_details_mock.side_effect = [no_drives, one_drive, two_drives]
raid_info = {'logical_disks': [{'size_gb': 50,
'raid_level': '1',
'disk_type': 'ssd'},
{'size_gb': 100,
'raid_level': '5',
'disk_type': 'hdd'}]}
exc = self.assertRaises(exception.PhysicalDisksNotFoundError,
manager.create_configuration,
raid_info)
self.assertIn("of size 50 GB and raid level 1", str(exc))
@mock.patch.object(objects.Controller, 'execute_cmd')
def test_delete_configuration(self, controller_exec_cmd_mock,
get_all_details_mock):
@@ -144,3 +195,18 @@ class ManagerTestCases(testtools.TestCase):
ld1_returned['root_device_hint'])
self.assertEqual(sorted(ld1_expected['physical_disks']),
sorted(ld1_returned['physical_disks']))
class RaidConfigValidationTestCases(testtools.TestCase):
def test_validate_fails_min_disks_number(self):
raid_config = {'size_gb': 100, 'raid_level': 5,
'number_of_physical_disks': 2}
self.assertRaises(exception.InvalidInputError,
manager.validate, raid_config)
def test_validate_fails_min_physical_disks(self):
raid_config = {'size_gb': 100, 'raid_level': 5,
'physical_disks': ['foo']}
self.assertRaises(exception.InvalidInputError,
manager.validate, raid_config)

View File

@@ -17,8 +17,8 @@ from oslo.concurrency import processutils
import testtools
from proliantutils import exception
from proliantutils.hpssa import constants
from proliantutils.hpssa import objects
from proliantutils.hpssa import types
from proliantutils.tests.hpssa import raid_constants
@@ -53,10 +53,10 @@ class ServerTest(testtools.TestCase):
physical_drive = filter(lambda x: x.id == '5I:1:1',
controller.unassigned_physical_drives)[0]
self.assertEqual(controller, physical_drive.parent)
self.assertEqual(600, physical_drive.size_gb)
self.assertEqual(types.INTERFACE_TYPE_SAS,
self.assertEqual(500, physical_drive.size_gb)
self.assertEqual(constants.INTERFACE_TYPE_SAS,
physical_drive.interface_type)
self.assertEqual(types.DISK_TYPE_HDD,
self.assertEqual(constants.DISK_TYPE_HDD,
physical_drive.disk_type)
def test_server_object_one_logical_drive(self, get_all_details_mock):
@@ -82,20 +82,20 @@ class ServerTest(testtools.TestCase):
self.assertEqual('1', logical_drive.id)
self.assertEqual(logical_drive.parent, array)
self.assertEqual(558, logical_drive.size_gb)
self.assertEqual(types.RAID_1, logical_drive.raid_level)
self.assertEqual(constants.RAID_1, logical_drive.raid_level)
self.assertIsInstance(logical_drive.properties, dict)
# Assertion on physical drives of array
physical_drive = filter(lambda x: x.id == '5I:1:1',
array.physical_drives)[0]
self.assertEqual(array, physical_drive.parent)
self.assertEqual(600, physical_drive.size_gb)
self.assertEqual(500, physical_drive.size_gb)
# Assertion on physical drives of controller
physical_drive = filter(lambda x: x.id == '5I:1:3',
controller.unassigned_physical_drives)[0]
self.assertEqual(controller, physical_drive.parent)
self.assertEqual(600, physical_drive.size_gb)
self.assertEqual(400, physical_drive.size_gb)
def test_get_controller_by_id(self, get_all_details_mock):