Add new get_resource method for rsd-lib 2.1

Add a get_resource method to return a resource instance from a uri, currently
there is no way for users to get rsd resource from a uri. This is helpful
for cases where users get the path from links and want to access the resource
using given path.

Change-Id: I51d4ead04d829631d37cb3bad8dd8bcf1194db3f
This commit is contained in:
leizhang 2019-05-31 19:44:53 +08:00 committed by Lin Yang
parent e05a709a70
commit 642827b727
4 changed files with 285 additions and 0 deletions

30
rsd_lib/exceptions.py Normal file
View File

@ -0,0 +1,30 @@
# Copyright 2019 Intel, Inc.
# 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.
class RsdlibError(Exception):
"""Basic exception for errors raised by rsd-lib"""
message = None
def __init__(self, **kwargs):
if self.message and kwargs:
self.message = self.message % kwargs
super(RsdlibError, self).__init__(self.message)
class NoMatchingResourceError(RsdlibError):
message = "No matching resource for the uri %(uri)s"

View File

@ -13,8 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
from sushy import exceptions
from sushy.resources import base
from rsd_lib import exceptions as rsd_lib_exceptions
from rsd_lib.resources.v2_1.chassis import chassis
from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch
from rsd_lib.resources.v2_1.event_service import event_service
@ -25,6 +27,7 @@ from rsd_lib.resources.v2_1.registries import message_registry_file
from rsd_lib.resources.v2_1.storage_service import storage_service
from rsd_lib.resources.v2_1.system import system
from rsd_lib.resources.v2_1.task import task_service
from rsd_lib.resources.v2_1 import types
class RSDLibV2_1(base.ResourceBase):
@ -279,3 +282,34 @@ class RSDLibV2_1(base.ResourceBase):
self._event_service_path,
redfish_version=self.redfish_version,
)
def _get_resource_class_from_path(self, path):
"""Get resource class from a given path
:param path: Path of any rsd resource
:returns: Corresponding resource class
"""
body = self._conn.get(path=path).json()
if not body.get("@odata.type"):
raise exceptions.MissingAttributeError(
attribute="@odata.type", resource=path
)
# Normally the format of resource_type is '#{namespace}.{entity_type}'
# Here we use entity_type to find the corresponding resource class
entity_type = body["@odata.type"].split(".")[-1]
return types.RESOURCE_CLASS.get(entity_type)
def get_resource(self, path):
"""Return corresponding resource object from path
:param path: The path of a resource or resource collection
:returns: corresponding resource or resource collection object
"""
resource_class = self._get_resource_class_from_path(path)
if not resource_class:
raise rsd_lib_exceptions.NoMatchingResourceError(uri=path)
return resource_class(
self._conn, path, redfish_version=self.redfish_version
)

View File

@ -0,0 +1,163 @@
# Copyright 2019 Intel, Inc.
# 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.
from rsd_lib.resources.v2_1.chassis import chassis
from rsd_lib.resources.v2_1.chassis import log_entry
from rsd_lib.resources.v2_1.chassis import log_service
from rsd_lib.resources.v2_1.chassis import power
from rsd_lib.resources.v2_1.chassis import power_zone
from rsd_lib.resources.v2_1.chassis import thermal
from rsd_lib.resources.v2_1.chassis import thermal_zone
from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch
from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch_acl
from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch_acl_rule
from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch_port
from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch_static_mac
from rsd_lib.resources.v2_1.ethernet_switch import vlan_network_interface
from rsd_lib.resources.v2_1.event_service import event_destination
from rsd_lib.resources.v2_1.event_service import event_service
from rsd_lib.resources.v2_1.fabric import endpoint
from rsd_lib.resources.v2_1.fabric import fabric
from rsd_lib.resources.v2_1.fabric import port
from rsd_lib.resources.v2_1.fabric import switch
from rsd_lib.resources.v2_1.fabric import zone
from rsd_lib.resources.v2_1.manager import manager
from rsd_lib.resources.v2_1.manager import manager_network_protocol
from rsd_lib.resources.v2_1.manager import serial_interface
from rsd_lib.resources.v2_1.manager import virtual_media
from rsd_lib.resources.v2_1.node import node
from rsd_lib.resources.v2_1.registries import message_registry_file
from rsd_lib.resources.v2_1.storage_service import logical_drive
from rsd_lib.resources.v2_1.storage_service import physical_drive
from rsd_lib.resources.v2_1.storage_service import remote_target
from rsd_lib.resources.v2_1.storage_service import storage_service
from rsd_lib.resources.v2_1.system import drive
from rsd_lib.resources.v2_1.system import ethernet_interface
from rsd_lib.resources.v2_1.system import memory
from rsd_lib.resources.v2_1.system import network_device_function
from rsd_lib.resources.v2_1.system import network_interface
from rsd_lib.resources.v2_1.system import pcie_device
from rsd_lib.resources.v2_1.system import pcie_function
from rsd_lib.resources.v2_1.system import processor
from rsd_lib.resources.v2_1.system import simple_storage
from rsd_lib.resources.v2_1.system import storage
from rsd_lib.resources.v2_1.system import system
from rsd_lib.resources.v2_1.system import volume
from rsd_lib.resources.v2_1.task import task
from rsd_lib.resources.v2_1.task import task_service
RESOURCE_CLASS = {
'Chassis': chassis.Chassis,
'ChassisCollection': chassis.ChassisCollection,
'.ComposedNode': node.Node,
'ComposedNodeCollection': node.NodeCollection,
'ComputerSystem': system.System,
'ComputerSystemCollection': system.SystemCollection,
'Drive': drive.Drive,
'Endpoint': endpoint.Endpoint,
'EndpointCollection': endpoint.EndpointCollection,
'EthernetInterface': ethernet_interface.EthernetInterface,
'EthernetInterfaceCollection':
ethernet_interface.EthernetInterfaceCollection,
'EthernetSwitch': ethernet_switch.EthernetSwitch,
'EthernetSwitchACL': ethernet_switch_acl.EthernetSwitchACL,
'EthernetSwitchACLCollection':
ethernet_switch_acl.EthernetSwitchACLCollection,
'EthernetSwitchACLRule': ethernet_switch_acl_rule.EthernetSwitchACLRule,
'EthernetSwitchACLRuleCollection':
ethernet_switch_acl_rule.EthernetSwitchACLRuleCollection,
'EthernetSwitchCollection': ethernet_switch.EthernetSwitchCollection,
'EthernetSwitchPort': ethernet_switch_port.EthernetSwitchPort,
'EthernetSwitchPortCollection':
ethernet_switch_port.EthernetSwitchPortCollection,
'EthernetSwitchStaticMAC':
ethernet_switch_static_mac.EthernetSwitchStaticMAC,
'EthernetSwitchStaticMACCollection':
ethernet_switch_static_mac.EthernetSwitchStaticMACCollection,
'EventDestination': event_destination.EventDestination,
'EventDestinationCollection': event_destination.EventDestinationCollection,
'EventService': event_service.EventService,
'Fabric': fabric.Fabric,
'FabricCollection': fabric.FabricCollection,
'LogEntry': log_entry.LogEntry,
'LogEntryCollection': log_entry.LogEntryCollection,
'LogService': log_service.LogService,
'LogServiceCollection': log_service.LogServiceCollection,
'LogicalDrive': logical_drive.LogicalDrive,
'LogicalDriveCollection': logical_drive.LogicalDriveCollection,
'Manager': manager.Manager,
'ManagerCollection': manager.ManagerCollection,
'ManagerNetworkProtocol': manager_network_protocol.ManagerNetworkProtocol,
'Memory': memory.Memory,
'MemoryCollection': memory.MemoryCollection,
'MessageRegistryFile': message_registry_file.MessageRegistryFile,
'MessageRegistryFileCollection':
message_registry_file.MessageRegistryFileCollection,
'NetworkDeviceFunction': network_device_function.NetworkDeviceFunction,
'NetworkDeviceFunctionCollection':
network_device_function.NetworkDeviceFunctionCollection,
'NetworkInterface': network_interface.NetworkInterface,
'NetworkInterfaceCollection': network_interface.NetworkInterfaceCollection,
'PCIeDevice': pcie_device.PCIeDevice,
'PCIeFunction': pcie_function.PCIeFunction,
'PhysicalDrive': physical_drive.PhysicalDrive,
'PhysicalDriveCollection': physical_drive.PhysicalDriveCollection,
'Port': port.Port,
'PortCollection': port.PortCollection,
'Power': power.Power,
'PowerZone': power_zone.PowerZone,
'PowerZoneCollection': power_zone.PowerZoneCollection,
'Processor': processor.Processor,
'ProcessorCollection': processor.ProcessorCollection,
'RemoteTarget': remote_target.RemoteTarget,
'RemoteTargetCollection':
remote_target.RemoteTargetCollection,
'SerialInterface': serial_interface.SerialInterface,
'SerialInterfaceCollection': serial_interface.SerialInterfaceCollection,
'SimpleStorage': simple_storage.SimpleStorage,
'SimpleStorageCollection': simple_storage.SimpleStorageCollection,
'Storage': storage.Storage,
'StorageCollection': storage.StorageCollection,
'StorageService': storage_service.StorageService,
'StorageServiceCollection': storage_service.StorageServiceCollection,
'Switch': switch.Switch,
'SwitchCollection': switch.SwitchCollection,
'Task': task.Task,
'TaskCollection': task.TaskCollection,
'TaskService': task_service.TaskService,
'Thermal': thermal.Thermal,
'ThermalZone': thermal_zone.ThermalZone,
'ThermalZoneCollection':
thermal_zone.ThermalZoneCollection,
'VLanNetworkInterface':
vlan_network_interface.VLanNetworkInterface,
'VLanNetworkInterfaceCollection':
vlan_network_interface.VLanNetworkInterfaceCollection,
'VirtualMedia': virtual_media.VirtualMedia,
'VirtualMediaCollection': virtual_media.VirtualMediaCollection,
'Volume': volume.Volume,
'VolumeCollection': volume.VolumeCollection,
'Zone': zone.Zone,
'ZoneCollection': zone.ZoneCollection
}

View File

@ -15,8 +15,10 @@
import json
import mock
from sushy import exceptions
import testtools
from rsd_lib.exceptions import NoMatchingResourceError
from rsd_lib.resources import v2_1
from rsd_lib.resources.v2_1.chassis import chassis
from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch
@ -234,3 +236,59 @@ class RSDLibV2_1TestCase(testtools.TestCase):
"/redfish/v1/EventService",
redfish_version=self.rsd.redfish_version,
)
def test_get_resource_class_with_connection_error(self):
self.conn.get.side_effect = exceptions.ConnectionError()
self.assertRaises(
exceptions.ConnectionError,
self.rsd._get_resource_class_from_path,
"/redfish/v1/Chassis/1",
)
self.conn.reset()
def test_get_resource_class_from_path_with_invalid_response(self):
self.conn.get.return_value.json.return_value = {}
self.assertRaisesRegexp(
exceptions.MissingAttributeError,
"The attribute @odata.type is missing from the resource "
"/redfish/v1/Chassis/1",
self.rsd._get_resource_class_from_path,
"/redfish/v1/Chassis/1",
)
self.conn.reset()
def test_get_resource_class_from_path(self):
self.conn.get.return_value.json.return_value = {
"@odata.type": "#Chassis.v1_3_0.Chassis"
}
self.assertEqual(
chassis.Chassis,
self.rsd._get_resource_class_from_path("/redfish/v1/Chassis/1"),
)
self.conn.reset()
def test_get_resource(self):
with mock.patch.object(
self.rsd,
"_get_resource_class_from_path",
return_value=chassis.Chassis,
):
with open(
"rsd_lib/tests/unit/json_samples/v2_1/chassis.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(
f.read()
)
self.assertIsInstance(
self.rsd.get_resource("/redfish/v1/Chassis/1"), chassis.Chassis
)
def test_get_resource_with_no_class_match(self):
with mock.patch.object(
self.rsd, "_get_resource_class_from_path", return_value=None
):
self.assertRaises(
NoMatchingResourceError,
self.rsd.get_resource,
"/redfish/v1/chassis/1",
)