Add System<->Manager linkage
Redfish data model rests on three interlinked entities - ComputerSystem(s), Manager(s) and Chassis. As of this moment, sushy does not support traversing between these entities at the Sushy abstraction level, despite the availability of such linkage in the JSON documents sushy feeds on. This change establishes System->Managers and Managers->System links. Change-Id: I54b0fdeebdea1e13c2b6912ee4c97776ebccaf03 Story: 2004512 Task: 28240
This commit is contained in:
parent
5fbc00ed91
commit
0bf7cbf14d
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Establishes ComputerSystem->Managers and Manager->ComputerSystems
|
||||
references at sushy data abstraction level what make it possible to
|
||||
look up Manager(s) responsible for a ComputerSystem and vice versa.
|
|
@ -188,6 +188,25 @@ class Manager(base.ResourceBase):
|
|||
self._conn, utils.get_sub_resource_path_by(self, 'VirtualMedia'),
|
||||
redfish_version=self.redfish_version)
|
||||
|
||||
@property
|
||||
@utils.cache_it
|
||||
def systems(self):
|
||||
"""A list of systems managed by this manager.
|
||||
|
||||
Returns a list of `System` objects representing systems being
|
||||
managed by this manager.
|
||||
|
||||
:raises: MissingAttributeError if '@odata.id' field is missing.
|
||||
:returns: A list of `System` instances
|
||||
"""
|
||||
paths = utils.get_sub_resource_path_by(
|
||||
self, ["Links", "ManagerForServers"], is_collection=True)
|
||||
|
||||
from sushy.resources.system import system
|
||||
return [system.System(self._conn, path,
|
||||
redfish_version=self.redfish_version)
|
||||
for path in paths]
|
||||
|
||||
|
||||
class ManagerCollection(base.ResourceCollectionBase):
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import logging
|
|||
from sushy import exceptions
|
||||
from sushy.resources import base
|
||||
from sushy.resources import common
|
||||
from sushy.resources.manager import manager
|
||||
from sushy.resources import mappings as res_maps
|
||||
from sushy.resources.system import bios
|
||||
from sushy.resources.system import constants as sys_cons
|
||||
|
@ -333,6 +334,24 @@ class System(base.ResourceBase):
|
|||
self._conn, utils.get_sub_resource_path_by(self, "Storage"),
|
||||
redfish_version=self.redfish_version)
|
||||
|
||||
@property
|
||||
@utils.cache_it
|
||||
def managers(self):
|
||||
"""A list of managers for this system.
|
||||
|
||||
Returns a list of `Manager` objects representing the managers
|
||||
that manage this system.
|
||||
|
||||
:raises: MissingAttributeError if '@odata.id' field is missing.
|
||||
:returns: A list of `Manager` instances
|
||||
"""
|
||||
paths = utils.get_sub_resource_path_by(
|
||||
self, ["Links", "ManagedBy"], is_collection=True)
|
||||
|
||||
return [manager.Manager(self._conn, path,
|
||||
redfish_version=self.redfish_version)
|
||||
for path in paths]
|
||||
|
||||
|
||||
class SystemCollection(base.ResourceCollectionBase):
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import sushy
|
|||
from sushy import exceptions
|
||||
from sushy.resources.manager import manager
|
||||
from sushy.resources.manager import virtual_media
|
||||
from sushy.resources.system import system
|
||||
from sushy.tests.unit import base
|
||||
|
||||
|
||||
|
@ -265,6 +266,18 @@ class ManagerTestCase(base.TestCase):
|
|||
virtual_media.VirtualMediaCollection)
|
||||
self.assertFalse(vrt_media._is_stale)
|
||||
|
||||
def test_systems(self):
|
||||
# | GIVEN |
|
||||
with open('sushy/tests/unit/json_samples/'
|
||||
'system.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
|
||||
# | WHEN & THEN |
|
||||
actual_systems = self.manager.systems
|
||||
self.assertIsInstance(actual_systems[0], system.System)
|
||||
self.assertEqual(
|
||||
'/redfish/v1/Systems/437XR1138R2', actual_systems[0].path)
|
||||
|
||||
|
||||
class ManagerCollectionTestCase(base.TestCase):
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import mock
|
|||
import sushy
|
||||
from sushy import exceptions
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy.resources.manager import manager
|
||||
from sushy.resources.system import bios
|
||||
from sushy.resources.system import mappings as sys_map
|
||||
from sushy.resources.system import processor
|
||||
|
@ -478,6 +479,18 @@ class SystemTestCase(base.TestCase):
|
|||
# | WHEN & THEN |
|
||||
self.assertIsInstance(self.sys_inst.storage, storage.StorageCollection)
|
||||
|
||||
def test_managers(self):
|
||||
# | GIVEN |
|
||||
with open('sushy/tests/unit/json_samples/'
|
||||
'manager.json') as f:
|
||||
self.conn.get.return_value.json.return_value = json.load(f)
|
||||
|
||||
# | WHEN & THEN |
|
||||
actual_managers = self.sys_inst.managers
|
||||
self.assertIsInstance(actual_managers[0], manager.Manager)
|
||||
self.assertEqual(
|
||||
'/redfish/v1/Managers/BMC', actual_managers[0].path)
|
||||
|
||||
|
||||
class SystemCollectionTestCase(base.TestCase):
|
||||
|
||||
|
|
|
@ -69,6 +69,14 @@ class UtilsTestCase(base.TestCase):
|
|||
subresource_path)
|
||||
self.assertEqual(expected_result, value)
|
||||
|
||||
def test_get_sub_resource_path_by_collection(self):
|
||||
subresource_path = ["Links", "ManagedBy"]
|
||||
expected_result = ['/redfish/v1/Managers/BMC']
|
||||
value = utils.get_sub_resource_path_by(self.sys_inst,
|
||||
subresource_path,
|
||||
is_collection=True)
|
||||
self.assertEqual(expected_result, value)
|
||||
|
||||
def test_get_sub_resource_path_by_fails(self):
|
||||
subresource_path = ['Links', 'Chassis']
|
||||
expected_result = 'attribute Links/Chassis/@odata.id is missing'
|
||||
|
|
|
@ -66,13 +66,17 @@ def int_or_none(x):
|
|||
return int(x)
|
||||
|
||||
|
||||
def get_sub_resource_path_by(resource, subresource_name):
|
||||
def get_sub_resource_path_by(resource, subresource_name, is_collection=False):
|
||||
"""Helper function to find the subresource path
|
||||
|
||||
:param resource: ResourceBase instance on which the name
|
||||
gets queried upon.
|
||||
:param subresource_name: name of the resource field to
|
||||
fetch the '@odata.id' from.
|
||||
:param is_collection: if `True`, expect a list of resources to
|
||||
fetch the '@odata.id' from.
|
||||
:returns: Resource path (if `is_collection` is `False`) or
|
||||
a list of resource paths (if `is_collection` is `True`).
|
||||
"""
|
||||
if not subresource_name:
|
||||
raise ValueError('"subresource_name" cannot be empty')
|
||||
|
@ -88,12 +92,24 @@ def get_sub_resource_path_by(resource, subresource_name):
|
|||
raise exceptions.MissingAttributeError(
|
||||
attribute='/'.join(subresource_name), resource=resource.path)
|
||||
|
||||
if '@odata.id' not in body:
|
||||
raise exceptions.MissingAttributeError(
|
||||
attribute='/'.join(subresource_name) + '/@odata.id',
|
||||
resource=resource.path)
|
||||
elements = []
|
||||
|
||||
return body['@odata.id']
|
||||
try:
|
||||
if is_collection:
|
||||
for element in body:
|
||||
elements.append(element['@odata.id'])
|
||||
return elements
|
||||
|
||||
else:
|
||||
return body['@odata.id']
|
||||
|
||||
except (TypeError, KeyError):
|
||||
attribute = '/'.join(subresource_name)
|
||||
if is_collection:
|
||||
attribute += '[%s]' % len(elements)
|
||||
attribute += '/@odata.id'
|
||||
raise exceptions.MissingAttributeError(
|
||||
attribute=attribute, resource=resource.path)
|
||||
|
||||
|
||||
def max_safe(iterable, default=0):
|
||||
|
|
Loading…
Reference in New Issue