diff --git a/doc/source/client-api.rst b/doc/source/client-api.rst index 4bdb405..ed52618 100644 --- a/doc/source/client-api.rst +++ b/doc/source/client-api.rst @@ -170,6 +170,10 @@ list_memory ~~~~~~~~~~~ Returns a list of installed memory modules. +list_nics +~~~~~~~~~ +Returns a list of NICs. + Job management -------------- diff --git a/dracclient/client.py b/dracclient/client.py index 580a529..c9f0434 100644 --- a/dracclient/client.py +++ b/dracclient/client.py @@ -451,6 +451,18 @@ class DRACClient(object): return self._inventory_mgmt.list_memory() + def list_nics(self): + """Returns a list of NICs + + :returns: a list of NIC objects + :raises: WSManRequestFailure on request failures + :raises: WSManInvalidResponse when receiving invalid response + :raises: DRACOperationFailed on error reported back by the DRAC + interface + """ + + return self._inventory_mgmt.list_nics() + class WSManClient(wsman.Client): """Wrapper for wsman.Client with return value checking""" diff --git a/dracclient/constants.py b/dracclient/constants.py index 33efbf7..52bcd14 100644 --- a/dracclient/constants.py +++ b/dracclient/constants.py @@ -15,3 +15,6 @@ POWER_ON = 'POWER_ON' POWER_OFF = 'POWER_OFF' REBOOT = 'REBOOT' + +# binary unit constants +UNITS_KI = 2 ** 10 diff --git a/dracclient/resources/inventory.py b/dracclient/resources/inventory.py index 7272236..44fc681 100644 --- a/dracclient/resources/inventory.py +++ b/dracclient/resources/inventory.py @@ -13,25 +13,55 @@ import collections +from dracclient import constants from dracclient.resources import uris from dracclient import utils -CPU = collections.namedtuple( - 'CPU', - ['id', 'cores', 'speed', 'ht_enabled', 'model', 'status', 'turbo_enabled', - 'vt_enabled']) - -Memory = collections.namedtuple( - 'Memory', - ['id', 'size', 'speed', 'manufacturer', 'model', 'status']) - -PrimaryStatus = { +PRIMARY_STATUS = { '0': 'Unknown', '1': 'OK', '2': 'Degraded', '3': 'Error' } +NIC_LINK_SPEED_MBPS = { + '0': None, + '1': 10, + '2': 100, + '3': 1000, + '4': 2.5 * constants.UNITS_KI, + '5': 10 * constants.UNITS_KI, + '6': 20 * constants.UNITS_KI, + '7': 40 * constants.UNITS_KI, + '8': 100 * constants.UNITS_KI, + '9': 25 * constants.UNITS_KI, + '10': 50 * constants.UNITS_KI +} + +NIC_LINK_DUPLEX = { + '0': 'unknown', + '1': 'full duplex', + '2': 'half duplex' +} + +NIC_MODE = { + '0': 'unknown', + '2': 'enabled', + '3': 'disabled'} + +CPU = collections.namedtuple( + 'CPU', + ['id', 'cores', 'speed_mhz', 'ht_enabled', 'model', 'status', + 'turbo_enabled', 'vt_enabled']) + +Memory = collections.namedtuple( + 'Memory', + ['id', 'size', 'speed_mhz', 'manufacturer', 'model', 'status']) + +NIC = collections.namedtuple( + 'NIC', + ['id', 'mac', 'model', 'speed_mbps', 'duplex', 'media_type']) + class InventoryManagement(object): @@ -63,10 +93,10 @@ class InventoryManagement(object): return CPU( id=self._get_cpu_attr(cpu, 'FQDD'), cores=int(self._get_cpu_attr(cpu, 'NumberOfProcessorCores')), - speed=int(self._get_cpu_attr(cpu, 'CurrentClockSpeed')), + speed_mhz=int(self._get_cpu_attr(cpu, 'CurrentClockSpeed')), ht_enabled=bool(self._get_cpu_attr(cpu, 'HyperThreadingEnabled')), model=self._get_cpu_attr(cpu, 'Model'), - status=PrimaryStatus[self._get_cpu_attr(cpu, 'PrimaryStatus')], + status=PRIMARY_STATUS[self._get_cpu_attr(cpu, 'PrimaryStatus')], turbo_enabled=bool(self._get_cpu_attr(cpu, 'TurboModeEnabled')), vt_enabled=bool(self._get_cpu_attr(cpu, 'VirtualizationTechnologyEnabled')) @@ -96,14 +126,47 @@ class InventoryManagement(object): def _parse_memory(self, memory): return Memory(id=self._get_memory_attr(memory, 'FQDD'), size=int(self._get_memory_attr(memory, 'Size')), - speed=int(self._get_memory_attr(memory, 'Speed')), + speed_mhz=int(self._get_memory_attr(memory, 'Speed')), manufacturer=self._get_memory_attr(memory, 'Manufacturer'), model=self._get_memory_attr(memory, 'Model'), - status=PrimaryStatus[self._get_memory_attr( + status=PRIMARY_STATUS[self._get_memory_attr( memory, 'PrimaryStatus')]) def _get_memory_attr(self, memory, attr_name): return utils.get_wsman_resource_attr(memory, uris.DCIM_MemoryView, attr_name) + + def list_nics(self): + """Returns the list of NICs + + :returns: a list of NIC objects + :raises: WSManRequestFailure on request failures + :raises: WSManInvalidResponse when receiving invalid response + :raises: DRACOperationFailed on error reported back by the DRAC + interface + """ + + doc = self.client.enumerate(uris.DCIM_NICView) + drac_nics = utils.find_xml(doc, 'DCIM_NICView', uris.DCIM_NICView, + find_all=True) + + return [self._parse_drac_nic(nic) for nic in drac_nics] + + def _parse_drac_nic(self, drac_nic): + fqdd = self._get_nic_attr(drac_nic, 'FQDD') + drac_speed = self._get_nic_attr(drac_nic, 'LinkSpeed') + drac_duplex = self._get_nic_attr(drac_nic, 'LinkDuplex') + + return NIC( + id=fqdd, + mac=self._get_nic_attr(drac_nic, 'CurrentMACAddress'), + model=self._get_nic_attr(drac_nic, 'ProductName'), + speed_mbps=NIC_LINK_SPEED_MBPS[drac_speed], + duplex=NIC_LINK_DUPLEX[drac_duplex], + media_type=self._get_nic_attr(drac_nic, 'MediaType')) + + def _get_nic_attr(self, drac_nic, attr_name): + return utils.get_wsman_resource_attr(drac_nic, uris.DCIM_NICView, + attr_name) diff --git a/dracclient/resources/uris.py b/dracclient/resources/uris.py index 7ef1194..b1dea17 100644 --- a/dracclient/resources/uris.py +++ b/dracclient/resources/uris.py @@ -49,6 +49,9 @@ DCIM_LifecycleJob = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/' DCIM_MemoryView = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/' 'DCIM_MemoryView') +DCIM_NICView = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/' + 'DCIM_NICView') + DCIM_PhysicalDiskView = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/' 'DCIM_PhysicalDiskView') diff --git a/dracclient/tests/test_client.py b/dracclient/tests/test_client.py index 05e204e..905ceb5 100644 --- a/dracclient/tests/test_client.py +++ b/dracclient/tests/test_client.py @@ -1046,6 +1046,92 @@ class ClientRAIDManagementTestCase(base.BaseTest): cim_name='DCIM:RAIDService', target='controller') +@requests_mock.Mocker() +class ClientInventoryManagementTestCase(base.BaseTest): + + def setUp(self): + super(ClientInventoryManagementTestCase, self).setUp() + self.drac_client = dracclient.client.DRACClient( + **test_utils.FAKE_ENDPOINT) + + def test_list_cpus(self, mock_requests): + expected_cpu = [inventory.CPU( + id='CPU.Socket.1', + cores=6, + speed_mhz=2400, + ht_enabled=True, + model='Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz', + status='OK', + turbo_enabled=True, + vt_enabled=True + )] + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.InventoryEnumerations[uris.DCIM_CPUView]['ok']) + + self.assertEqual( + expected_cpu, + self.drac_client.list_cpus()) + + def test_list_memory(self, mock_requests): + expected_memory = [inventory.Memory( + id='DIMM.Socket.A1', + size=16384, + speed_mhz=2133, + manufacturer='Samsung', + model='DDR4 DIMM', + status='OK', + )] + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.InventoryEnumerations[uris.DCIM_MemoryView]['ok']) + + self.assertEqual( + expected_memory, + self.drac_client.list_memory()) + + def test_list_nics(self, mock_requests): + expected_nics = [ + inventory.NIC( + id='NIC.Embedded.1-1-1', + mac='B0:83:FE:C6:6F:A1', + model='Broadcom Gigabit Ethernet BCM5720 - B0:83:FE:C6:6F:A1', + speed_mbps=1000, + duplex='full duplex', + media_type='Base T'), + inventory.NIC( + id='NIC.Slot.2-1-1', + mac='A0:36:9F:52:7D:1E', + model='Intel(R) Gigabit 2P I350-t Adapter - A0:36:9F:52:7D:1E', + speed_mbps=1000, + duplex='full duplex', + media_type='Base T'), + inventory.NIC( + id='NIC.Slot.2-2-1', + mac='A0:36:9F:52:7D:1F', + model='Intel(R) Gigabit 2P I350-t Adapter - A0:36:9F:52:7D:1F', + speed_mbps=1000, + duplex='full duplex', + media_type='Base T'), + inventory.NIC( + id='NIC.Embedded.2-1-1', + mac='B0:83:FE:C6:6F:A2', + model='Broadcom Gigabit Ethernet BCM5720 - B0:83:FE:C6:6F:A2', + speed_mbps=1000, + duplex='full duplex', + media_type='Base T')] + + mock_requests.post( + 'https://1.2.3.4:443/wsman', + text=test_utils.InventoryEnumerations[uris.DCIM_NICView]['ok']) + + self.assertEqual( + expected_nics, + self.drac_client.list_nics()) + + @requests_mock.Mocker() class WSManClientTestCase(base.BaseTest): @@ -1110,59 +1196,3 @@ class WSManClientTestCase(base.BaseTest): self.assertRaises(exceptions.DRACUnexpectedReturnValue, client.invoke, 'http://resource', 'Foo', expected_return_value='4242') - - -@requests_mock.Mocker() -class ClientCPUTestCase(base.BaseTest): - - def setUp(self): - super(ClientCPUTestCase, self).setUp() - self.drac_client = dracclient.client.DRACClient( - **test_utils.FAKE_ENDPOINT) - - def test_list_cpus(self, mock_requests): - expected_cpu = [inventory.CPU( - id='CPU.Socket.1', - cores=6, - speed=2400, - ht_enabled=True, - model='Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz', - status='OK', - turbo_enabled=True, - vt_enabled=True - )] - - mock_requests.post( - 'https://1.2.3.4:443/wsman', - text=test_utils.CPUEnumerations[uris.DCIM_CPUView]['ok']) - - self.assertEqual( - expected_cpu, - self.drac_client.list_cpus()) - - -@requests_mock.Mocker() -class ClientMemoryestCase(base.BaseTest): - - def setUp(self): - super(ClientMemoryestCase, self).setUp() - self.drac_client = dracclient.client.DRACClient( - **test_utils.FAKE_ENDPOINT) - - def test_list_memory(self, mock_requests): - expected_memory = [inventory.Memory( - id='DIMM.Socket.A1', - size=16384, - speed=2133, - manufacturer='Samsung', - model='DDR4 DIMM', - status='OK', - )] - - mock_requests.post( - 'https://1.2.3.4:443/wsman', - text=test_utils.MemoryEnumerations[uris.DCIM_MemoryView]['ok']) - - self.assertEqual( - expected_memory, - self.drac_client.list_memory()) diff --git a/dracclient/tests/utils.py b/dracclient/tests/utils.py index 47d590a..7310765 100644 --- a/dracclient/tests/utils.py +++ b/dracclient/tests/utils.py @@ -97,9 +97,15 @@ BIOSInvocations = { } } -CPUEnumerations = { +InventoryEnumerations = { uris.DCIM_CPUView: { 'ok': load_wsman_xml('cpu-enumeration-enum-ok') + }, + uris.DCIM_MemoryView: { + 'ok': load_wsman_xml('memory-enumeration-enum-ok') + }, + uris.DCIM_NICView: { + 'ok': load_wsman_xml('nic_view-enum-ok') } } @@ -133,12 +139,6 @@ LifecycleControllerEnumerations = { }, } -MemoryEnumerations = { - uris.DCIM_MemoryView: { - 'ok': load_wsman_xml('memory-enumeration-enum-ok') - }, -} - RAIDEnumerations = { uris.DCIM_ControllerView: { 'ok': load_wsman_xml('controller_view-enum-ok') diff --git a/dracclient/tests/wsman_mocks/nic_view-enum-ok.xml b/dracclient/tests/wsman_mocks/nic_view-enum-ok.xml new file mode 100644 index 0000000..656a0d3 --- /dev/null +++ b/dracclient/tests/wsman_mocks/nic_view-enum-ok.xml @@ -0,0 +1,189 @@ + + + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + http://schemas.xmlsoap.org/ws/2004/09/enumeration/EnumerateResponse + uuid:c31a0567-63f6-4a58-8379-b4ea037a18a6 + uuid:94e955b1-371f-171f-94f8-a36fc6fe83b0 + + + + + + 2 + 2 + 1.34 + B0:83:FE:C6:6F:A1 + 0002 + Embedded NIC 1 Port 1 Partition 1 + 0 + 16.4.14 + 3 + + NIC.Embedded.1-1-1 + 7.10.17 + 0 + NIC.Embedded.1-1-1 + 20151126211205.000000+000 + 20141017011300.000000+000 + 1 + 3 + 0 + Base T + 0 + 3 + 165f + 04f7 + 1028 + 14e4 + + B0:83:FE:C6:6F:A1 + + Broadcom Gigabit Ethernet BCM5720 - B0:83:FE:C6:6F:A1 + 2 + 0002 + 0002 + 2 + Broadcom Corp + + + + + 3 + + + 2 + 8 + + A0:36:9F:52:7D:1E + 000D + NIC in Slot 2 Port 1 Partition 1 + 0 + + 3 + + NIC.Slot.2-1-1 + 16.0.22 + 0 + NIC.Slot.2-1-1 + 20151126211205.000000+000 + 20150114205015.000000+000 + 1 + 3 + 0 + Base T + 0 + 3 + 1521 + 5002 + 8086 + 8086 + + A0:36:9F:52:7D:1E + + Intel(R) Gigabit 2P I350-t Adapter - A0:36:9F:52:7D:1E + 2 + 0004 + 00B1 + 3 + Intel Corp + + + + + 3 + + + 2 + 8 + + A0:36:9F:52:7D:1F + 000D + NIC in Slot 2 Port 2 Partition 1 + 0 + + 3 + + NIC.Slot.2-2-1 + 16.0.22 + 1 + NIC.Slot.2-2-1 + 20151126211205.000000+000 + 20150114205015.000000+000 + 1 + 3 + 0 + Base T + 0 + 3 + 1521 + 5002 + 8086 + 8086 + + A0:36:9F:52:7D:1F + + Intel(R) Gigabit 2P I350-t Adapter - A0:36:9F:52:7D:1F + 2 + 0004 + 00B1 + 3 + Intel Corp + + + + + 3 + + + 2 + 2 + 1.34 + B0:83:FE:C6:6F:A2 + 0002 + Embedded NIC 2 Port 1 Partition 1 + 0 + 16.4.14 + 3 + + NIC.Embedded.2-1-1 + 7.10.17 + 1 + NIC.Embedded.2-1-1 + 20151126211205.000000+000 + 20150114205015.000000+000 + 1 + 3 + 0 + Base T + 0 + 3 + 165f + 04f7 + 1028 + 14e4 + + B0:83:FE:C6:6F:A2 + + Broadcom Gigabit Ethernet BCM5720 - B0:83:FE:C6:6F:A2 + 2 + 0002 + 0002 + 2 + Broadcom Corp + + + + + 3 + + + + + + + \ No newline at end of file