diff --git a/requirements.txt b/requirements.txt index 366f321..e863bce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,3 +20,4 @@ oslo.i18n>=2.1.0 # Apache-2.0 python-ironicclient>=1.11.0 # Apache-2.0 python-keystoneclient>=3.8.0 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0 +sushy>=0.1.0 # Apache-2.0 diff --git a/valence/podmanagers/__init__.py b/valence/podmanagers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/valence/podmanagers/podm_base.py b/valence/podmanagers/podm_base.py new file mode 100644 index 0000000..30b4491 --- /dev/null +++ b/valence/podmanagers/podm_base.py @@ -0,0 +1,54 @@ +# Copyright (c) 2016 Intel, Inc. +# +# 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 valence.redfish.sushy import sushy_instance + + +class PodManagerBase(object): + + def __init__(self, username, password, podm_url): + self.podm_url = podm_url + self.driver = sushy_instance.RedfishInstance(username=username, + password=password, + base_url=podm_url) + + def get_podm_info(self): + return self.get_resource_info_by_url(self.podm_url) + + def get_resource_info_by_url(self, resource_url): + return self.driver.get_resources_by_url(resource_url) + + def get_chassis_collection(self): + chassis_collection_url = self.podm_url + '/Chassis' + return self.driver.get_resources_by_url(chassis_collection_url) + + def get_chassis_info(self, chassis_id): + chassis_url = self.podm_url + '/Chassis/' + chassis_id + return self.driver.get_resources_by_url(chassis_url) + + def get_system_collection(self): + system_collection_url = self.podm_url + '/Systems' + return self.driver.get_resources_by_url(system_collection_url) + + def get_system_info(self, system_id): + system_url = self.podm_url + '/Systems/' + system_id + return self.driver.get_resources_by_url(system_url) + + def get_node_collection(self): + node_collection_url = self.podm_url + '/Nodes' + return self.driver.get_resources_by_url(node_collection_url) + + def get_node_info(self, node_id): + node_url = self.podm_url + '/Nodes/' + node_id + return self.driver.get_resources_by_url(node_url) diff --git a/valence/redfish/sushy/__init__.py b/valence/redfish/sushy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/valence/redfish/sushy/resources/__init__.py b/valence/redfish/sushy/resources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/valence/redfish/sushy/resources/chassis.py b/valence/redfish/sushy/resources/chassis.py new file mode 100644 index 0000000..dd520d4 --- /dev/null +++ b/valence/redfish/sushy/resources/chassis.py @@ -0,0 +1,83 @@ +# Copyright 2017 Red Hat, 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 logging + +from sushy.resources import base + +LOG = logging.getLogger(__name__) + + +class Chassis(base.ResourceBase): + asset_tag = base.Field('AssetTag') + """The chassis asset tag""" + + description = base.Field('Description') + """The chassis description""" + + identity = base.Field('Id', required=True) + """The chassis identity string""" + + manufacturer = base.Field('Manufacturer') + """The chassis manufacturer""" + + name = base.Field('Name') + """The chassis name""" + + part_number = base.Field('PartNumber') + """The chassis part number""" + + serial_number = base.Field('SerialNumber') + """The chassis serial number""" + + sku = base.Field('SKU') + """The chassis stock-keeping unit""" + + chassis_type = base.Field('ChassisType') + """The chassis type""" + + oem = base.Field('Oem') + """The chassis oem options values (dict)""" + + def __init__(self, connector, identity, redfish_version=None): + """A class representing a Chassis + + :param connector: A Connector instance + :param identity: The identity of the chassis resource + :param redfish_version: The version of RedFish. Used to construct + the object according to schema of the given version. + """ + super(Chassis, self).__init__(connector, identity, redfish_version) + + def refresh(self): + super(Chassis, self).refresh() + + +class ChassisCollection(base.ResourceCollectionBase): + @property + def _resource_type(self): + return Chassis + + def __init__(self, connector, path, redfish_version=None): + """A class representing a ComputerchassisCollection + + :param connector: A Connector instance + :param path: The canonical path to the chassis collection resource + :param redfish_version: The version of RedFish. Used to construct + the object according to schema of the given version. + """ + super(ChassisCollection, self).__init__(connector, + path, + redfish_version) diff --git a/valence/redfish/sushy/sushy_instance.py b/valence/redfish/sushy/sushy_instance.py new file mode 100644 index 0000000..80033f3 --- /dev/null +++ b/valence/redfish/sushy/sushy_instance.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# Copyright (c) 2016 Intel, Inc. +# +# 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 sushy +from sushy import exceptions + +from resources import chassis + + +class RedfishInstance(sushy.Sushy): + + def __init__(self, base_url, username, password): + """A class representing a Sushy Object Instance + + :param base_url: The base URL to the Redfish controller. It + should include scheme and authority portion of the URL. + For example: https://valence.podm:443/ + :param username: User account with admin/server-profile access + privilege + :param password: User account password + """ + super(RedfishInstance, self).__init__(base_url=base_url, + username=username, + password=password) + + def _get_chassis_collection_path(self): + """Helper function to find the Chassis Collection path""" + chassis_col = self.json.get('Chassis') + if not chassis_col: + raise exceptions.MissingAttributeError(attribute='Chassis', + resource=self._path) + return chassis_col.get('@odata.id') + + def get_chassis_collection(self): + """Get the Chassis Collection object + + :returns: a Chassis Collection object + """ + return chassis.ChassisCollection(self._conn, + self._get_chassis_collection_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) diff --git a/valence/tests/unit/redfish/sushy/__init__.py b/valence/tests/unit/redfish/sushy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/valence/tests/unit/redfish/sushy/json_samples/chassis.json b/valence/tests/unit/redfish/sushy/json_samples/chassis.json new file mode 100644 index 0000000..8ffc965 --- /dev/null +++ b/valence/tests/unit/redfish/sushy/json_samples/chassis.json @@ -0,0 +1,73 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Chassis/Members/$entity", + "@odata.id": "/redfish/v1/Chassis/Chassis1", + "@odata.type": "#Chassis.1.0.0.Chassis", + "AssetTag": "FlexChassis1", + "ChassisType": "Drawer", + "Description": "this is a chassis", + "Id": "Chassis1", + "IndicatorLED": "On", + "Links": { + "ComputerSystems": [ + { + "@odata.id": "/redfish/v1/Systems/system1" + }, + { + "@odata.id": "/redfish/v1/Systems/system2" + }, + { + "@odata.id": "/redfish/v1/Systems/system3" + }, + { + "@odata.id": "/redfish/v1/Systems/system4" + } + ], + "ContainedBy": { + "@odata.id": "/redfish/v1/Chassis/Rack1" + }, + "Contains": [ + { + "@odata.id": "/redfish/v1/Chassis/Chassis1" + } + ], + "ManagedBy": [ + { + "@odata.id": "/redfish/v1/Managers/manager1" + } + ], + "ManagersIn": [ + { + "@odata.id": "/redfish/v1/Managers/manager1" + } + ], + "Oem": { }, + "Switches": [ + { + "@odata.id": "/redfish/v1/EthernetSwitches/switch1" + } + ] + }, + "Manufacturer": "Intel Corporaion", + "Model": "Lenovo FLEX 8731", + "Name": "FLEX-1", + "Oem": { + "Intel:RackScale": { + "@odata.type": "#Intel.Oem.Chassis", + "Location": { + "Rack": "Rack1", + "UHeight": "10 U", + "ULocation": "25 U", + "UWidth": "1 U" + }, + "UUID": "e1c2d764-5c72-36d6-9945-a78255edab51" + } + }, + "PartNumber": "5c72-36d6", + "SKU": "e1c2d764-5c72", + "SerialNumber": "a78255edab51", + "Status": { + "Health": "OK", + "HealthRollup": "OK", + "State": "Enabled" + } +} \ No newline at end of file diff --git a/valence/tests/unit/redfish/sushy/json_samples/chassis_collection.json b/valence/tests/unit/redfish/sushy/json_samples/chassis_collection.json new file mode 100644 index 0000000..f17392d --- /dev/null +++ b/valence/tests/unit/redfish/sushy/json_samples/chassis_collection.json @@ -0,0 +1,34 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Chassis", + "@odata.id": "/redfish/v1/Chassis", + "@odata.type": "#ChassisCollection.ChassisCollection", + "Members": [ + { + "@odata.id": "/redfish/v1/Chassis/Rack1" + }, + { + "@odata.id": "/redfish/v1/Chassis/Rack2" + }, + { + "@odata.id": "/redfish/v1/Chassis/Chassis1" + }, + { + "@odata.id": "/redfish/v1/Chassis/Chassis2" + }, + { + "@odata.id": "/redfish/v1/Chassis/Drawer1" + }, + { + "@odata.id": "/redfish/v1/Chassis/Drawer2" + }, + { + "@odata.id": "/redfish/v1/Chassis/NVMEChassis1" + }, + { + "@odata.id": "/redfish/v1/Chassis/NVMEChassis2" + } + ], + "Members@odata.count": 8, + "Name": "Chassis Collection" + +} \ No newline at end of file diff --git a/valence/tests/unit/redfish/sushy/resources/__init__.py b/valence/tests/unit/redfish/sushy/resources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/valence/tests/unit/redfish/sushy/resources/test_chassis.py b/valence/tests/unit/redfish/sushy/resources/test_chassis.py new file mode 100644 index 0000000..41fad9d --- /dev/null +++ b/valence/tests/unit/redfish/sushy/resources/test_chassis.py @@ -0,0 +1,88 @@ +# 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 + +from sushy.tests.unit import base + +from valence.redfish.sushy.resources import chassis + + +class TestChassis(base.TestCase): + + def setUp(self): + super(TestChassis, self).setUp() + self.conn = mock.Mock() + + with open('valence/tests/unit/redfish/sushy/json_samples/chassis.json', + 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.chassis_inst = chassis.Chassis(self.conn, + '/redfish/v1/Chassis/chassis1', + redfish_version='1.0.2') + + def test_parse_attributes(self): + self.chassis_inst._parse_attributes() + self.assertEqual('1.0.2', self.chassis_inst.redfish_version) + self.assertEqual('FlexChassis1', self.chassis_inst.asset_tag) + self.assertEqual('Drawer', self.chassis_inst.chassis_type) + self.assertEqual('this is a chassis', self.chassis_inst.description) + self.assertEqual('Chassis1', self.chassis_inst.identity) + self.assertEqual('Intel Corporaion', self.chassis_inst.manufacturer) + self.assertEqual('FLEX-1', self.chassis_inst.name) + self.assertEqual('5c72-36d6', self.chassis_inst.part_number) + self.assertEqual('a78255edab51', self.chassis_inst.serial_number) + self.assertEqual('e1c2d764-5c72', self.chassis_inst.sku) + self.assertEqual('e1c2d764-5c72-36d6-9945-a78255edab51', + self.chassis_inst.oem['Intel:RackScale']['UUID']) + + +class TestChassisCollection(base.TestCase): + + def setUp(self): + super(TestChassisCollection, self).setUp() + self.conn = mock.Mock() + + with open('valence/tests/unit/redfish/sushy/json_samples/' + 'chassis_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.chassis_col = chassis.ChassisCollection(self.conn, + '/redfish/v1/Systems', + redfish_version='1.0.2') + + def test__parse_attributes(self): + self.chassis_col._parse_attributes() + self.assertEqual('1.0.2', self.chassis_col.redfish_version) + self.assertEqual('Chassis Collection', self.chassis_col.name) + self.assertIn('/redfish/v1/Chassis/Chassis1', + self.chassis_col.members_identities) + + @mock.patch.object(chassis, 'Chassis', autospec=True) + def test_get_member(self, mock_chassis): + self.chassis_col.get_member('/redfish/v1/Chassis/Chassis1') + + mock_chassis.assert_called_once_with( + self.chassis_col._conn, + '/redfish/v1/Chassis/Chassis1', + redfish_version=self.chassis_col.redfish_version + ) + + @mock.patch.object(chassis, 'Chassis', autospec=True) + def test_get_members(self, mock_chassis): + members = self.chassis_col.get_members() + self.assertEqual(mock_chassis.call_count, 8) + self.assertIsInstance(members, list) + self.assertEqual(8, len(members))