diff --git a/dracclient/resources/raid.py b/dracclient/resources/raid.py
index d16501e..17de591 100644
--- a/dracclient/resources/raid.py
+++ b/dracclient/resources/raid.py
@@ -113,7 +113,7 @@ VirtualDiskTuple = collections.namedtuple(
'VirtualDisk',
['id', 'name', 'description', 'controller', 'raid_level', 'size_mb',
'status', 'raid_status', 'span_depth', 'span_length',
- 'pending_operations'])
+ 'pending_operations', 'physical_disks'])
class VirtualDisk(VirtualDiskTuple):
@@ -239,13 +239,19 @@ class RAIDManagement(object):
span_length=int(self._get_virtual_disk_attr(drac_disk,
'SpanLength')),
pending_operations=(
- VIRTUAL_DISK_PENDING_OPERATIONS[drac_pending_operations]))
+ VIRTUAL_DISK_PENDING_OPERATIONS[drac_pending_operations]),
+ physical_disks=self._get_virtual_disk_attrs(drac_disk,
+ 'PhysicalDiskIDs'))
def _get_virtual_disk_attr(self, drac_disk, attr_name, nullable=False):
return utils.get_wsman_resource_attr(
drac_disk, uris.DCIM_VirtualDiskView, attr_name,
nullable=nullable)
+ def _get_virtual_disk_attrs(self, drac_disk, attr_name):
+ return utils.get_all_wsman_resource_attrs(
+ drac_disk, uris.DCIM_VirtualDiskView, attr_name, nullable=False)
+
def list_physical_disks(self):
"""Returns the list of physical disks
diff --git a/dracclient/tests/test_raid.py b/dracclient/tests/test_raid.py
index f802db8..c4ce680 100644
--- a/dracclient/tests/test_raid.py
+++ b/dracclient/tests/test_raid.py
@@ -62,7 +62,11 @@ class ClientRAIDManagementTestCase(base.BaseTest):
raid_status='online',
span_depth=1,
span_length=2,
- pending_operations=None)
+ pending_operations=None,
+ physical_disks=[
+ 'Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1',
+ 'Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1'
+ ])
mock_requests.post(
'https://1.2.3.4:443/wsman',
@@ -452,7 +456,7 @@ class ClientRAIDManagementTestCase(base.BaseTest):
@mock.patch.object(dracclient.resources.job.JobManagement,
'delete_pending_config', spec_set=True, autospec=True)
- def test_abandon_pending_bios_changes(self, mock_requests,
+ def test_abandon_pending_raid_changes(self, mock_requests,
mock_delete_pending_config):
self.drac_client.abandon_pending_raid_changes('controller')
diff --git a/dracclient/tests/test_utils.py b/dracclient/tests/test_utils.py
index e99db8b..cad2744 100644
--- a/dracclient/tests/test_utils.py
+++ b/dracclient/tests/test_utils.py
@@ -27,6 +27,28 @@ class UtilsTestCase(base.BaseTest):
def setUp(self):
super(UtilsTestCase, self).setUp()
+ def test__is_attr_non_nil_True(self):
+ doc = etree.fromstring(
+ test_utils.RAIDEnumerations[
+ uris.DCIM_ControllerView]['ok'])
+ controllers = utils.find_xml(doc, 'DCIM_ControllerView',
+ uris.DCIM_ControllerView, find_all=True)
+ version = utils.find_xml(controllers[0], 'Bus',
+ uris.DCIM_ControllerView)
+
+ self.assertTrue(utils._is_attr_non_nil(version))
+
+ def test__is_attr_non_nil_False(self):
+ doc = etree.fromstring(
+ test_utils.RAIDEnumerations[
+ uris.DCIM_ControllerView]['ok'])
+ controllers = utils.find_xml(doc, 'DCIM_ControllerView',
+ uris.DCIM_ControllerView, find_all=True)
+ version = utils.find_xml(controllers[0], 'DriverVersion',
+ uris.DCIM_ControllerView)
+
+ self.assertFalse(utils._is_attr_non_nil(version))
+
def test_get_wsman_resource_attr(self):
doc = etree.fromstring(
test_utils.InventoryEnumerations[uris.DCIM_CPUView]['ok'])
@@ -79,3 +101,68 @@ class UtilsTestCase(base.BaseTest):
exceptions.DRACEmptyResponseField, re.escape(expected_message),
utils.get_wsman_resource_attr, cpus[0], uris.DCIM_CPUView,
'HyperThreadingEnabled', allow_missing=False)
+
+ def test_get_wsman_resource_attr_missing_text_allowed(self):
+ doc = etree.fromstring(
+ test_utils.RAIDEnumerations[
+ uris.DCIM_ControllerView]['ok'])
+ controllers = utils.find_xml(doc, 'DCIM_ControllerView',
+ uris.DCIM_ControllerView, find_all=True)
+
+ result = utils.get_wsman_resource_attr(
+ controllers[0], uris.DCIM_ControllerView, 'DriverVersion',
+ allow_missing=False, nullable=True)
+ self.assertIsNone(result)
+
+ def test_get_all_wsman_resource_attrs(self):
+ doc = etree.fromstring(
+ test_utils.RAIDEnumerations[uris.DCIM_VirtualDiskView]['ok'])
+ vdisks = utils.find_xml(doc, 'DCIM_VirtualDiskView',
+ uris.DCIM_VirtualDiskView, find_all=True)
+
+ vals = utils.get_all_wsman_resource_attrs(
+ vdisks[0], uris.DCIM_VirtualDiskView, 'PhysicalDiskIDs')
+
+ expected_pdisks = [
+ 'Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1',
+ 'Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1'
+ ]
+ self.assertListEqual(expected_pdisks, vals)
+
+ def test_get_all_wsman_resource_attrs_missing_attr_allowed(self):
+ doc = etree.fromstring(
+ test_utils.InventoryEnumerations[
+ uris.DCIM_CPUView]['missing_flags'])
+ cpus = utils.find_xml(doc, 'DCIM_CPUView', uris.DCIM_CPUView,
+ find_all=True)
+
+ vals = utils.get_all_wsman_resource_attrs(
+ cpus[0], uris.DCIM_CPUView, 'HyperThreadingEnabled')
+
+ self.assertListEqual([], vals)
+
+ def test_get_all_wsman_resource_attrs_missing_text(self):
+ expected_message = ("Attribute 'HyperThreadingEnabled' is not nullable"
+ ", but no value received")
+ doc = etree.fromstring(
+ test_utils.InventoryEnumerations[
+ uris.DCIM_CPUView]['empty_flag'])
+ cpus = utils.find_xml(doc, 'DCIM_CPUView', uris.DCIM_CPUView,
+ find_all=True)
+
+ self.assertRaisesRegexp(
+ exceptions.DRACEmptyResponseField, re.escape(expected_message),
+ utils.get_all_wsman_resource_attrs, cpus[0], uris.DCIM_CPUView,
+ 'HyperThreadingEnabled')
+
+ def test_get_all_wsman_resource_attrs_missing_text_allowed(self):
+ doc = etree.fromstring(
+ test_utils.RAIDEnumerations[
+ uris.DCIM_ControllerView]['ok'])
+ controllers = utils.find_xml(doc, 'DCIM_ControllerView',
+ uris.DCIM_ControllerView, find_all=True)
+
+ result = utils.get_all_wsman_resource_attrs(
+ controllers[0], uris.DCIM_ControllerView, 'DriverVersion',
+ nullable=True)
+ self.assertEqual(result, [])
diff --git a/dracclient/tests/wsman_mocks/virtual_disk_view-enum-ok.xml b/dracclient/tests/wsman_mocks/virtual_disk_view-enum-ok.xml
index 1777050..ee507f2 100644
--- a/dracclient/tests/wsman_mocks/virtual_disk_view-enum-ok.xml
+++ b/dracclient/tests/wsman_mocks/virtual_disk_view-enum-ok.xml
@@ -30,7 +30,8 @@
Background Intialization
8
0
-
+ Disk.Bay.0:Enclosure.Internal.0-1:RAID.Integrated.1-1
+ Disk.Bay.1:Enclosure.Internal.0-1:RAID.Integrated.1-1
1
2
4
@@ -51,4 +52,4 @@
-
\ No newline at end of file
+
diff --git a/dracclient/utils.py b/dracclient/utils.py
index 935cdb4..c7ddc82 100644
--- a/dracclient/utils.py
+++ b/dracclient/utils.py
@@ -46,6 +46,15 @@ def find_xml(doc, item, namespace, find_all=False):
return doc.find(query)
+def _is_attr_non_nil(elem):
+ """Return whether an element is non-nil.
+
+ :param elem: the element object.
+ :returns: whether the element is nil.
+ """
+ return elem.attrib.get('{%s}nil' % NS_XMLSchema_Instance) != 'true'
+
+
def get_wsman_resource_attr(doc, resource_uri, attr_name, nullable=False,
allow_missing=False):
"""Find an attribute of a resource in an ElementTree object.
@@ -78,11 +87,37 @@ def get_wsman_resource_attr(doc, resource_uri, attr_name, nullable=False,
raise exceptions.DRACEmptyResponseField(attr=attr_name)
return item.text.strip()
else:
- nil_attr = item.attrib.get('{%s}nil' % NS_XMLSchema_Instance)
- if nil_attr != 'true':
+ if _is_attr_non_nil(item):
return item.text.strip()
+def get_all_wsman_resource_attrs(doc, resource_uri, attr_name, nullable=False):
+ """Find all instances of an attribute of a resource in an ElementTree.
+
+ :param doc: the element tree object.
+ :param resource_uri: the resource URI of the namespace.
+ :param attr_name: the name of the attribute.
+ :param nullable: enables checking if any of the elements contain an
+ XMLSchema-instance namespaced nil attribute that has a
+ value of True. In this case, these elements will not be
+ returned.
+ :raises: DRACEmptyResponseField if any of the attributes in the XML doc
+ have no text and nullable is False.
+ :returns: a list containing the value of each of the instances of the
+ attribute.
+ """
+ items = find_xml(doc, attr_name, resource_uri, find_all=True)
+
+ if not nullable:
+ for item in items:
+ if item.text is None:
+ raise exceptions.DRACEmptyResponseField(attr=attr_name)
+ return [item.text.strip() for item in items]
+ else:
+
+ return [item.text.strip() for item in items if _is_attr_non_nil(item)]
+
+
def is_reboot_required(doc, resource_uri):
"""Check the response document if reboot is requested.