Redfish: Add the get_server_capabilities() and pci_gpu_device attribute

This patch adds the routine get_server_capabilities()
and one of the capability 'pci_gpu_devices' which gives
the count of gpu devices.

Change-Id: Iff38aa201b7a1658723d926292707e163c22c34d
This commit is contained in:
Nisha Agarwal 2017-07-10 03:24:39 +00:00 committed by ankit
parent d85ef6498c
commit ca5a0c973e
9 changed files with 309 additions and 0 deletions

View File

@ -593,3 +593,21 @@ class RedfishOperations(operations.IloOperations):
{'username': self._username, 'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)
def get_server_capabilities(self):
"""Returns the server capabilities
:raises: IloError if any Sushy error is encountered.
"""
capabilities = {}
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
try:
count = len(sushy_system.pci_devices.gpu_devices)
capabilities.update({'pci_gpu_devices': count})
except sushy.exceptions.SushyError as e:
msg = (self._("The Redfish controller is unable to get "
"resource or its members. Error"
"%(error)s)") % {'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)
return capabilities

View File

@ -0,0 +1,56 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# 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 logging
from sushy.resources import base
LOG = logging.getLogger(__name__)
CLASSCODE_FOR_GPU_DEVICES = [3]
SUBCLASSCODE_FOR_GPU_DEVICES = [0, 1, 2, 128]
class PCIDevice(base.ResourceBase):
identity = base.Field('Id', required=True)
name = base.Field('Name')
class_code = base.Field('ClassCode')
sub_class_code = base.Field('SubclassCode')
class PCIDeviceCollection(base.ResourceCollectionBase):
_gpu_devices = None
@property
def _resource_type(self):
return PCIDevice
@property
def gpu_devices(self):
if self._gpu_devices is None:
self._gpu_devices = []
for member in self.get_members():
if member.class_code in CLASSCODE_FOR_GPU_DEVICES:
if member.sub_class_code in SUBCLASSCODE_FOR_GPU_DEVICES:
self._gpu_devices.append(member)
return self._gpu_devices
def refresh(self):
super(PCIDeviceCollection, self).refresh()
self._gpu_devices = None

View File

@ -22,6 +22,7 @@ from proliantutils import exception
from proliantutils import log
from proliantutils.redfish.resources.system import bios
from proliantutils.redfish.resources.system import mappings
from proliantutils.redfish.resources.system import pci_device
from proliantutils.redfish import utils
LOG = log.get_logger(__name__)
@ -59,6 +60,8 @@ class HPESystem(system.System):
_bios_settings = None
_pci_devices = None
def _get_hpe_push_power_button_action_element(self):
push_action = self._hpe_actions.computer_system_ext_powerbutton
if not push_action:
@ -141,3 +144,20 @@ class HPESystem(system.System):
tenure = (sushy.BOOT_SOURCE_ENABLED_CONTINUOUS
if persistent else sushy.BOOT_SOURCE_ENABLED_ONCE)
self.set_system_boot_source(device, enabled=tenure)
@property
def pci_devices(self):
"""Provides the collection of PCI devices
It is calculated once when the first time it is queried. On refresh,
this property gets reset.
"""
if self._pci_devices is None:
self._pci_devices = pci_device.PCIDeviceCollection(
self._conn, utils.get_subresource_path_by(
self, ['Oem', 'Hpe', 'Links', 'PCIDevices']))
return self._pci_devices
def refresh(self):
super(HPESystem, self).refresh()
self._pci_devices = None

View File

@ -0,0 +1,25 @@
{
"@odata.context": "/redfish/v1/$metadata#Systems/Members/1/PCIDevices/Members/$entity",
"@odata.etag": "W/\"33150E20\"",
"@odata.id": "/redfish/v1/Systems/1/PCIDevices/1/",
"@odata.type": "#HpeServerPciDevice.v2_0_0.HpeServerPciDevice",
"BusNumber": 2,
"ClassCode": 2,
"DeviceID": 5719,
"DeviceInstance": 1,
"DeviceLocation": "Embedded",
"DeviceNumber": 0,
"DeviceSubInstance": 1,
"DeviceType": "Embedded LOM",
"FunctionNumber": 0,
"Id": "1",
"LocationString": "Embedded LOM 1",
"Name": "Network Controller",
"SegmentNumber": 0,
"StructuredName": "NIC.LOM.1.1",
"SubclassCode": 0,
"SubsystemDeviceID": 8894,
"SubsystemVendorID": 4156,
"UEFIDevicePath": "PciRoot(0x0)/Pci(0x1C,0x0)/Pci(0x0,0x0)",
"VendorID": 5348
}

View File

@ -0,0 +1,25 @@
{
"@odata.context": "/redfish/v1/$metadata#Systems/Members/1/PCIDevices/Members/$entity",
"@odata.etag": "W/\"5ABCB1B9\"",
"@odata.id": "/redfish/v1/Systems/1/PCIDevices/6/",
"@odata.type": "#HpeServerPciDevice.v2_0_0.HpeServerPciDevice",
"BusNumber": 1,
"ClassCode": 3,
"DeviceID": 1336,
"DeviceInstance": 1,
"DeviceLocation": "Embedded",
"DeviceNumber": 0,
"DeviceSubInstance": 1,
"DeviceType": "Video",
"FunctionNumber": 1,
"Id": "6",
"LocationString": "Embedded Video Controller",
"Name": "Embedded Video Controller",
"SegmentNumber": 0,
"StructuredName": "PCI.Emb.1.1",
"SubclassCode": 0,
"SubsystemDeviceID": 228,
"SubsystemVendorID": 5520,
"UEFIDevicePath": "PciRoot(0x0)/Pci(0x1C,0x4)/Pci(0x0,0x1)",
"VendorID": 4139
}

View File

@ -0,0 +1,17 @@
{
"@odata.context": "/redfish/v1/$metadata#Systems/Members/1/PCIDevices",
"@odata.etag": "W/\"9975C252\"",
"@odata.id": "/redfish/v1/Systems/1/PCIDevices/",
"@odata.type": "#HpeServerPciDeviceCollection.HpeServerPciDeviceCollection",
"Description": " PciDevices view",
"Members": [
{
"@odata.id": "/redfish/v1/Systems/1/PCIDevices/1/"
},
{
"@odata.id": "/redfish/v1/Systems/1/PCIDevices/6/"
}
],
"Members@odata.count": 2,
"Name": "PciDevices"
}

View File

@ -0,0 +1,102 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
# All Rights Reserved.
#
# 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 json
import mock
import testtools
from proliantutils.redfish.resources.system import pci_device
class PCIDeviceTestCase(testtools.TestCase):
def setUp(self):
super(PCIDeviceTestCase, self).setUp()
self.conn = mock.Mock()
pci_file = 'proliantutils/tests/redfish/json_samples/pci_device.json'
with open(pci_file, 'r') as f:
self.conn.get.return_value.json.return_value = (
json.loads(f.read()))
pci_path = "/redfish/v1/Systems/1/PCIDevices/1"
self.sys_pci = pci_device.PCIDevice(
self.conn, pci_path, redfish_version='1.0.2')
def test__parse_attributes(self):
self.sys_pci._parse_attributes()
self.assertEqual('1.0.2', self.sys_pci.redfish_version)
self.assertEqual('1', self.sys_pci.identity)
class PCIDeviceCollectionTestCase(testtools.TestCase):
def setUp(self):
super(PCIDeviceCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('proliantutils/tests/redfish/json_samples/'
'pci_device_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.sys_pci_col = pci_device.PCIDeviceCollection(
self.conn, '/redfish/v1/Systems/1/PCIDevices',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.sys_pci_col._parse_attributes()
self.assertEqual('1.0.2', self.sys_pci_col.redfish_version)
self.assertEqual('PciDevices', self.sys_pci_col.name)
pci_path = ('/redfish/v1/Systems/1/PCIDevices/1',
'/redfish/v1/Systems/1/PCIDevices/6')
self.assertEqual(pci_path, self.sys_pci_col.members_identities)
@mock.patch.object(pci_device, 'PCIDevice', autospec=True)
def test_get_member(self, mock_pci):
self.sys_pci_col.get_member(
'/redfish/v1/Systems/1/PCIDevices/1')
mock_pci.assert_called_once_with(
self.sys_pci_col._conn,
('/redfish/v1/Systems/1/PCIDevices/1'),
redfish_version=self.sys_pci_col.redfish_version)
@mock.patch.object(pci_device, 'PCIDevice', autospec=True)
def test_get_members(self, mock_pci):
members = self.sys_pci_col.get_members()
path_list = ["/redfish/v1/Systems/1/PCIDevices/1",
"/redfish/v1/Systems/1/PCIDevices/6"]
calls = [
mock.call(self.sys_pci_col._conn, path_list[0],
redfish_version=self.sys_pci_col.redfish_version),
mock.call(self.sys_pci_col._conn, path_list[1],
redfish_version=self.sys_pci_col.redfish_version)
]
mock_pci.assert_has_calls(calls)
self.assertIsInstance(members, list)
self.assertEqual(2, len(members))
def test_gpu_devices(self):
self.assertIsNone(self.sys_pci_col._gpu_devices)
self.conn.get.return_value.json.reset_mock()
val = []
path = ('proliantutils/tests/redfish/json_samples/'
'pci_device.json')
with open(path, 'r') as f:
val.append(json.loads(f.read()))
path = ('proliantutils/tests/redfish/json_samples/'
'pci_device1.json')
with open(path, 'r') as f:
val.append(json.loads(f.read()))
self.conn.get.return_value.json.side_effect = val
self.sys_pci_col.gpu_devices
self.assertEqual(1, len(self.sys_pci_col._gpu_devices))

View File

@ -145,3 +145,27 @@ class HPESystemTestCase(testtools.TestCase):
exception.IloError,
'The BIOS Boot Settings was not found.',
self.sys_inst.update_persistent_boot, ['ISCSI'], True, '12345678')
def test_pci_devices(self):
pci_dev_return_value = None
pci_dev1_return_value = None
pci_coll_return_value = None
self.assertIsNone(self.sys_inst._pci_devices)
self.conn.get.return_value.json.reset_mock()
with open('proliantutils/tests/redfish/'
'json_samples/pci_device_collection.json') as f:
pci_coll_return_value = json.loads(f.read())
with open('proliantutils/tests/redfish/'
'json_samples/pci_device.json') as f:
pci_dev_return_value = json.loads(f.read())
with open('proliantutils/tests/redfish/'
'json_samples/pci_device1.json') as f:
pci_dev1_return_value = json.loads(f.read())
self.conn.get.return_value.json.side_effect = (
[pci_coll_return_value, pci_dev_return_value,
pci_dev1_return_value])
actual_pci = self.sys_inst.pci_devices
self.conn.get.return_value.json.reset_mock()
self.assertIs(actual_pci,
self.sys_inst.pci_devices)
self.conn.get.return_value.json.assert_not_called()

View File

@ -645,3 +645,25 @@ class RedfishOperationsTestCase(testtools.TestCase):
exception.IloError,
'No account found with username: foo',
self.rf_client.reset_ilo_credential, 'fake-password')
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
def test_get_server_capabilities(self, get_system_mock):
val = []
path = ('proliantutils/tests/redfish/json_samples/'
'pci_device.json')
with open(path, 'r') as f:
val.append(json.loads(f.read()))
gpu_mock = mock.PropertyMock(return_value=val)
type(get_system_mock.return_value.pci_devices).gpu_devices = (
gpu_mock)
actual = self.rf_client.get_server_capabilities()
expected = {'pci_gpu_devices': 1}
self.assertEqual(expected, actual)
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_system')
def test_get_server_capabilities_gpu_fail(self, get_system_mock):
gpu_mock = mock.PropertyMock(side_effect=sushy.exceptions.SushyError)
type(get_system_mock.return_value.pci_devices).gpu_devices = (
gpu_mock)
self.assertRaises(exception.IloError,
self.rf_client.get_server_capabilities)