Add multiply version support

Now it will automatically detect the RSD PODM API version and create
corresponding object for it. This will be easy to add more version
support in the future.

Change-Id: I18039ef1a4aeebb4176aae1d6162de0f8f9e2123
This commit is contained in:
Lin Yang 2018-01-29 19:59:28 -08:00
parent d7f2bd9967
commit 50d8947ca4
33 changed files with 306 additions and 146 deletions

View File

@ -13,7 +13,7 @@ Composing and using a logical node
# Get a connection with the RSD endpoint
rsd = rsd_lib.RSDLib('http://localhost:8443/redfish/v1',
username='foo', password='bar')
username='foo', password='bar').factory()
# Get the node collection object
node_col = rsd.get_node_collection()

View File

@ -3,5 +3,5 @@
# process, which may cause wedges in the gate later.
pbr>=2.0 # Apache-2.0
sushy<=1.2.0 # Apache-2.0
sushy>=1.2.0 # Apache-2.0
jsonschema<3.0.0,>=2.6.0 # MIT

View File

@ -13,111 +13,59 @@
# License for the specific language governing permissions and limitations
# under the License.
import sushy
from distutils import version
from sushy import connector
from sushy.resources import base
from rsd_lib.resources.chassis import chassis
from rsd_lib.resources.fabric import fabric
from rsd_lib.resources.node import node
from rsd_lib.resources.storage_service import storage_service
from rsd_lib.resources import v2_1
class RSDLib(sushy.Sushy):
class RSDLib(base.ResourceBase):
_nodes_path = base.Field(['Nodes', '@odata.id'], required=True)
"""NodeCollection path"""
_chassis_path = base.Field(['Chassis', '@odata.id'], required=True)
"""ChassisCollection path"""
_storage_service_path = base.Field(['Services',
'@odata.id'], required=True)
"""StorageServiceCollection path"""
_fabrics_path = base.Field(['Fabrics', '@odata.id'], required=True)
_redfish_version = base.Field(['RedfishVersion'], required=True)
"""FabricCollection path"""
_rsd_api_version = base.Field(['Oem', 'Intel_RackScale', 'ApiVersion'],
required=True)
"""RSD API version"""
def get_node_collection(self):
"""Get the NodeCollection object
def __init__(self, base_url, username=None, password=None,
root_prefix='/redfish/v1/', verify=True):
"""A class representing a RootService
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a NodeCollection object
:param base_url: The base URL to the Redfish controller. It
should include scheme and authority portion of the URL. For
example: https://mgmt.vendor.com
:param username: User account with admin/server-profile access
privilege
:param password: User account password
:param root_prefix: The default URL prefix. This part includes
the root service and version. Defaults to /redfish/v1
:param verify: Either a boolean value, a path to a CA_BUNDLE
file or directory with certificates of trusted CAs. If set to
True the driver will verify the host certificates; if False
the driver will ignore verifying the SSL certificate; if it's
a path the driver will use the specified certificate or one of
the certificates in the directory. Defaults to True.
"""
return node.NodeCollection(self._conn, self._nodes_path,
redfish_version=self.redfish_version)
self._root_prefix = root_prefix
super(RSDLib, self).__init__(
connector.Connector(base_url, username, password, verify),
path=self._root_prefix)
def get_node(self, identity):
"""Given the identity return a Node object
def factory(self):
"""Return different resource module according to RSD API Version
:param identity: The identity of the Node resource
:returns: The Node object
:returns: a resource module
"""
return node.Node(self._conn, identity,
redfish_version=self.redfish_version)
def get_storage_service_collection(self):
"""Get the StorageServiceCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a StorageServiceCollection object
"""
return storage_service.StorageServiceCollection(
self._conn, self._storage_service_path,
redfish_version=self.redfish_version)
def get_storage_service(self, identity):
"""Given the identity return a StorageService object
:param identity: The identity of the StorageService resource
:returns: The StorageService object
"""
return storage_service.StorageService(
self._conn, identity,
redfish_version=self.redfish_version)
def get_chassis_collection(self):
"""Get the ChassisCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a ChassisCollection object
"""
return chassis.ChassisCollection(self._conn,
self._chassis_path,
redfish_version=self.redfish_version)
def get_chassis(self, identity):
"""Given the identity return a Chassis object
:param identity: The identity of the Chassis resource
:returns: The Chassis object
"""
return chassis.Chassis(self._conn,
identity,
redfish_version=self.redfish_version)
def get_fabric_collection(self):
"""Get the FabricCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a FabricCollection object
"""
return fabric.FabricCollection(self._conn,
self._fabrics_path,
redfish_version=self.redfish_version)
def get_fabric(self, identity):
"""Given the identity return a Fabric object
:param identity: The identity of the Fabric resource
:returns: The Fabric object
"""
return fabric.Fabric(self._conn,
identity,
redfish_version=self.redfish_version)
rsd_version = version.StrictVersion(self._rsd_api_version)
if rsd_version <= version.StrictVersion("2.1.0"):
# Use the interface of RSD API 2.1.0 to interact with RSD 2.1.0 and
# all previous version.
return v2_1.RSDLibV2_1(self._conn, self._root_prefix,
redfish_version=self._redfish_version)
else:
raise NotImplementedError(
"The rsd-lib library doesn't support RSD API "
"version {0}.".format(self._rsd_api_version))

View File

@ -0,0 +1,157 @@
# Copyright 2017 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 sushy.resources import base
from sushy.resources.system import system
from rsd_lib.resources.v2_1.chassis import chassis
from rsd_lib.resources.v2_1.fabric import fabric
from rsd_lib.resources.v2_1.node import node
from rsd_lib.resources.v2_1.storage_service import storage_service
class RSDLibV2_1(base.ResourceBase):
_nodes_path = base.Field(['Nodes', '@odata.id'], required=True)
"""NodeCollection path"""
_chassis_path = base.Field(['Chassis', '@odata.id'], required=True)
"""ChassisCollection path"""
_storage_service_path = base.Field(['Services',
'@odata.id'], required=True)
"""StorageServiceCollection path"""
_fabrics_path = base.Field(['Fabrics', '@odata.id'], required=True)
"""FabricCollection path"""
_redfish_version = base.Field(['RedfishVersion'], required=True)
"""FabricCollection path"""
_rsd_api_version = base.Field(['Oem', 'Intel_RackScale', 'ApiVersion'],
required=True)
"""RSD API version"""
def __init__(self, connector, identity="/redfish/v1/",
redfish_version=None):
"""A class representing a ComposedNode
:param connector: A Connector instance
:param identity: The identity of the Node resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(RSDLibV2_1, self).__init__(connector, identity, redfish_version)
def get_system_collection(self):
"""Get the SystemCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a SystemCollection object
"""
return system.SystemCollection(self._conn, self._systems_path,
redfish_version=self.redfish_version)
def get_system(self, identity):
"""Given the identity return a System object
:param identity: The identity of the System resource
:returns: The System object
"""
return system.System(self._conn, identity,
redfish_version=self.redfish_version)
def get_node_collection(self):
"""Get the NodeCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a NodeCollection object
"""
return node.NodeCollection(self._conn, self._nodes_path,
redfish_version=self.redfish_version)
def get_node(self, identity):
"""Given the identity return a Node object
:param identity: The identity of the Node resource
:returns: The Node object
"""
return node.Node(self._conn, identity,
redfish_version=self.redfish_version)
def get_storage_service_collection(self):
"""Get the StorageServiceCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a StorageServiceCollection object
"""
return storage_service.StorageServiceCollection(
self._conn, self._storage_service_path,
redfish_version=self.redfish_version)
def get_storage_service(self, identity):
"""Given the identity return a StorageService object
:param identity: The identity of the StorageService resource
:returns: The StorageService object
"""
return storage_service.StorageService(
self._conn, identity,
redfish_version=self.redfish_version)
def get_chassis_collection(self):
"""Get the ChassisCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a ChassisCollection object
"""
return chassis.ChassisCollection(self._conn,
self._chassis_path,
redfish_version=self.redfish_version)
def get_chassis(self, identity):
"""Given the identity return a Chassis object
:param identity: The identity of the Chassis resource
:returns: The Chassis object
"""
return chassis.Chassis(self._conn,
identity,
redfish_version=self.redfish_version)
def get_fabric_collection(self):
"""Get the FabricCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a FabricCollection object
"""
return fabric.FabricCollection(self._conn,
self._fabrics_path,
redfish_version=self.redfish_version)
def get_fabric(self, identity):
"""Given the identity return a Fabric object
:param identity: The identity of the Fabric resource
:returns: The Fabric object
"""
return fabric.Fabric(self._conn,
identity,
redfish_version=self.redfish_version)

View File

@ -18,8 +18,8 @@ import logging
from sushy import exceptions
from sushy.resources import base
from rsd_lib.resources.fabric import endpoint
from rsd_lib.resources.fabric import zone
from rsd_lib.resources.v2_1.fabric import endpoint
from rsd_lib.resources.v2_1.fabric import zone
LOG = logging.getLogger(__name__)

View File

@ -18,7 +18,7 @@ import logging
from sushy.resources import base
from sushy import utils
from rsd_lib.resources.fabric import endpoint
from rsd_lib.resources.v2_1.fabric import endpoint
LOG = logging.getLogger(__name__)

View File

@ -15,7 +15,7 @@
from sushy import utils
from rsd_lib.resources.node import constants as node_cons
from rsd_lib.resources.v2_1.node import constants as node_cons
RESET_NODE_VALUE_MAP = {
'On': node_cons.RESET_ON,

View File

@ -22,9 +22,9 @@ from sushy.resources import common
from sushy.resources.system import system
from sushy import utils
from rsd_lib.resources.node import constants as node_cons
from rsd_lib.resources.node import mappings as node_maps
from rsd_lib.resources.node import schemas as node_schemas
from rsd_lib.resources.v2_1.node import constants as node_cons
from rsd_lib.resources.v2_1.node import mappings as node_maps
from rsd_lib.resources.v2_1.node import schemas as node_schemas
LOG = logging.getLogger(__name__)

View File

@ -18,9 +18,9 @@ import logging
from sushy import exceptions
from sushy.resources import base
from rsd_lib.resources.storage_service import logical_drive
from rsd_lib.resources.storage_service import physical_drive
from rsd_lib.resources.storage_service import remote_target
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
LOG = logging.getLogger(__name__)

View File

@ -42,7 +42,7 @@
"Oem": {
"Intel_RackScale": {
"@odata.type": "#Intel.Oem.ServiceRoot",
"ApiVersion": "2.2"
"ApiVersion": "2.1.0"
}
},
"@odata.context": "/redfish/v1/$metadata#ServiceRoot",

View File

@ -16,7 +16,7 @@ import mock
from sushy.tests.unit import base
from rsd_lib.resources.chassis import chassis
from rsd_lib.resources.v2_1.chassis import chassis
class TestChassis(base.TestCase):

View File

@ -18,7 +18,7 @@ import json
import mock
import testtools
from rsd_lib.resources.fabric import endpoint
from rsd_lib.resources.v2_1.fabric import endpoint
class EndpointTestCase(testtools.TestCase):

View File

@ -19,9 +19,9 @@ import mock
from sushy import exceptions
import testtools
from rsd_lib.resources.fabric import endpoint
from rsd_lib.resources.fabric import fabric
from rsd_lib.resources.fabric import zone
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 zone
class FabricTestCase(testtools.TestCase):

View File

@ -18,7 +18,7 @@ import json
import mock
import testtools
from rsd_lib.resources.fabric import zone
from rsd_lib.resources.v2_1.fabric import zone
class ZoneTestCase(testtools.TestCase):

View File

@ -21,8 +21,8 @@ import testtools
from sushy import exceptions
from sushy.resources.system import system
from rsd_lib.resources.node import constants as node_cons
from rsd_lib.resources.node import node
from rsd_lib.resources.v2_1.node import constants as node_cons
from rsd_lib.resources.v2_1.node import node
from rsd_lib.tests.unit.fakes import request_fakes

View File

@ -19,10 +19,10 @@ import mock
from sushy import exceptions
import testtools
from rsd_lib.resources.storage_service import logical_drive
from rsd_lib.resources.storage_service import physical_drive
from rsd_lib.resources.storage_service import remote_target
from rsd_lib.resources.storage_service import storage_service
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
class StorageServiceTestCase(testtools.TestCase):

View File

@ -0,0 +1,66 @@
# Copyright 2018 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.
import json
import mock
import testtools
from rsd_lib.resources import v2_1
from rsd_lib.resources.v2_1.fabric import fabric
from rsd_lib.resources.v2_1.node import node
class RSDLibV2_1TestCase(testtools.TestCase):
def setUp(self):
super(RSDLibV2_1TestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/root.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.rsd = v2_1.RSDLibV2_1(self.conn)
def test__parse_attributes(self):
self.rsd._parse_attributes()
self.assertEqual("2.1.0", self.rsd._rsd_api_version)
self.assertEqual("1.0.2", self.rsd._redfish_version)
@mock.patch.object(node, 'NodeCollection', autospec=True)
def test_get_node_collection(self, mock_node_collection):
self.rsd.get_node_collection()
mock_node_collection.assert_called_once_with(
self.rsd._conn, '/redfish/v1/Nodes',
redfish_version=self.rsd.redfish_version)
@mock.patch.object(node, 'Node', autospec=True)
def test_get_node(self, mock_node):
self.rsd.get_node('fake-node-id')
mock_node.assert_called_once_with(
self.rsd._conn, 'fake-node-id',
redfish_version=self.rsd.redfish_version)
@mock.patch.object(fabric, 'FabricCollection', autospec=True)
def test_get_fabric_collection(self, mock_fabric_collection):
self.rsd.get_fabric_collection()
mock_fabric_collection.assert_called_once_with(
self.rsd._conn, '/redfish/v1/Fabrics',
redfish_version=self.rsd.redfish_version)
@mock.patch.object(fabric, 'Fabric', autospec=True)
def test_get_fabric(self, mock_fabric):
self.rsd.get_fabric('fake-fabric-id')
mock_fabric.assert_called_once_with(
self.rsd._conn, 'fake-fabric-id',
redfish_version=self.rsd.redfish_version)

View File

@ -20,8 +20,7 @@ from sushy import connector
import testtools
from rsd_lib import main
from rsd_lib.resources.fabric import fabric
from rsd_lib.resources.node import node
from rsd_lib.resources import v2_1
class RSDLibTestCase(testtools.TestCase):
@ -38,32 +37,22 @@ class RSDLibTestCase(testtools.TestCase):
def test__parse_attributes(self):
self.rsd._parse_attributes()
self.assertEqual("2.2", self.rsd._rsd_api_version)
self.assertEqual("2.1.0", self.rsd._rsd_api_version)
self.assertEqual("1.0.2", self.rsd._redfish_version)
@mock.patch.object(node, 'NodeCollection', autospec=True)
def test_get_node_collection(self, mock_node_collection):
self.rsd.get_node_collection()
mock_node_collection.assert_called_once_with(
self.rsd._conn, '/redfish/v1/Nodes',
redfish_version=self.rsd.redfish_version)
@mock.patch.object(v2_1, 'RSDLibV2_1', autospec=True)
def test_factory(self, mock_rsdlibv2_1):
self.rsd.factory()
mock_rsdlibv2_1.assert_called_once_with(
self.rsd._conn,
self.rsd._root_prefix,
redfish_version=self.rsd._redfish_version)
@mock.patch.object(node, 'Node', autospec=True)
def test_get_node(self, mock_node):
self.rsd.get_node('fake-node-id')
mock_node.assert_called_once_with(
self.rsd._conn, 'fake-node-id',
redfish_version=self.rsd.redfish_version)
def test_factory_unsupported_version(self):
self.rsd._rsd_api_version = "10.0.0"
expected_error_message = "The rsd-lib library doesn't support RSD "\
"API version 10.0.0."
@mock.patch.object(fabric, 'FabricCollection', autospec=True)
def test_get_fabric_collection(self, mock_fabric_collection):
self.rsd.get_fabric_collection()
mock_fabric_collection.assert_called_once_with(
self.rsd._conn, '/redfish/v1/Fabrics',
redfish_version=self.rsd.redfish_version)
@mock.patch.object(fabric, 'Fabric', autospec=True)
def test_get_fabric(self, mock_fabric):
self.rsd.get_fabric('fake-fabric-id')
mock_fabric.assert_called_once_with(
self.rsd._conn, 'fake-fabric-id',
redfish_version=self.rsd.redfish_version)
with self.assertRaisesRegex(NotImplementedError,
expected_error_message):
self.rsd.factory()