diff --git a/rsd_lib/resources/v2_1/manager/manager.py b/rsd_lib/resources/v2_1/manager/manager.py index 2f1657a..980b676 100644 --- a/rsd_lib/resources/v2_1/manager/manager.py +++ b/rsd_lib/resources/v2_1/manager/manager.py @@ -16,6 +16,7 @@ from sushy.resources import base from sushy import utils +from rsd_lib.resources.v2_1.manager import network_protocol from rsd_lib import utils as rsd_lib_utils @@ -96,10 +97,6 @@ class Manager(base.ResourceBase): firmware_version = base.Field('FirmwareVersion') """The manager firmware version""" - network_protocol = base.Field('NetworkProtocol', - adapter=rsd_lib_utils.get_resource_identity) - """Link to network protocol of this manager""" - ethernet_interfaces = base.Field( 'EthernetInterfaces', adapter=rsd_lib_utils.get_resource_identity) @@ -124,6 +121,22 @@ class Manager(base.ResourceBase): """ super(Manager, self).__init__(connector, identity, redfish_version) + def _get_network_protocol_path(self): + """Helper function to find the network protocol path""" + return utils.get_sub_resource_path_by(self, 'NetworkProtocol') + + @property + @utils.cache_it + def network_protocol(self): + """Property to provide reference to `network protocol` instance + + It is calculated once when it is queried for the first time. On + refresh, this property is reset. + """ + return network_protocol.NetworkProtocol( + self._conn, self._get_network_protocol_path(), + redfish_version=self.redfish_version) + class ManagerCollection(base.ResourceCollectionBase): @property diff --git a/rsd_lib/resources/v2_1/manager/network_protocol.py b/rsd_lib/resources/v2_1/manager/network_protocol.py new file mode 100644 index 0000000..cd7b242 --- /dev/null +++ b/rsd_lib/resources/v2_1/manager/network_protocol.py @@ -0,0 +1,93 @@ +# 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 import utils as rsd_lib_utils +from sushy.resources import base + + +class StatusField(base.CompositeField): + state = base.Field('State') + health = base.Field('Health') + health_rollup = base.Field('HealthRollup') + + +class ProtocolField(base.CompositeField): + protocol_enables = base.Field('ProtocolEnabled') + """Whether the protocol is enabled""" + + port = base.Field('Port', adapter=rsd_lib_utils.num_or_none) + """The port of all protocol""" + + +class SSDPField(ProtocolField): + notify_multicast_interval_seconds = base.Field( + 'NotifyMulticastIntervalSeconds', + adapter=rsd_lib_utils.num_or_none) + """The NotifyMulticast interval time""" + + notify_ttl = base.Field( + 'NotifyTTL', + adapter=rsd_lib_utils.num_or_none) + """The notify ttl""" + + notify_ipv6_scope = base.Field('NotifyIPv6Scope') + """The NotifyIPv6Scope""" + + +class NetworkProtocol(base.ResourceBase): + identify = base.Field('Id', required=True) + """The NetworkProtocol identity string""" + + name = base.Field('Name') + """The NetworkProtocol identity string""" + + description = base.Field('Description') + """The NetworkProtocol description""" + + status = StatusField('Status') + """The NetworkProtocol Status""" + + hostname = base.Field('HostName') + """The NetworkProtocol Hostname""" + + fqdn = base.Field('FQDN') + """The NetworkProtocol FQDN""" + + http = ProtocolField('HTTP') + """The HTTP protocol""" + + https = ProtocolField('HTTPS') + """The HTTPS protocol""" + + ipmi = ProtocolField('IPMI') + """The IPMI protocol""" + + ssh = ProtocolField('SSH') + """The SSH protocol""" + + snmp = ProtocolField('SNMP') + """The SNMP protocol""" + + virtual_media = ProtocolField('VirtualMedia') + """The VirtualMedia protocol""" + + ssdp = SSDPField('SSDP') + """The SSDP protocol""" + + telnet = ProtocolField('Telnet') + """The Telnet protocol""" + + kvm_ip = ProtocolField('KVMIP') + """The KVMIP protocol""" diff --git a/rsd_lib/tests/unit/json_samples/v2_1/network_protocol.json b/rsd_lib/tests/unit/json_samples/v2_1/network_protocol.json new file mode 100644 index 0000000..932bc57 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_1/network_protocol.json @@ -0,0 +1,55 @@ +{ + "@odata.context": "/redfish/v1/$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol", + "@odata.id": "/redfish/v1/Managers/BMC1/NetworkProtocol", + "@odata.type": "#ManagerNetworkProtocol.v1_0_0.ManagerNetworkProtocol", + "Id": "NetworkProtocol", + "Name": "Manager Network Protocol", + "Description": "Manager Network Service Status", + "Status": { + "State": "Enabled", + "Health": "OK", + "HealthRollup": null + }, + "HostName": "mymanager", + "FQDN": "mymanager.mydomain.com", + "HTTP": { + "ProtocolEnabled": true, + "Port": 80 + }, + "HTTPS": { + "ProtocolEnabled": true, + "Port": 443 + }, + "IPMI": { + "ProtocolEnabled": true, + "Port": 623 + }, + "SSH": { + "ProtocolEnabled": true, + "Port": 22 + }, + "SNMP": { + "ProtocolEnabled": true, + "Port": 161 + }, + "VirtualMedia": { + "ProtocolEnabled": true, + "Port": 17988 + }, + "SSDP": { + "ProtocolEnabled": true, + "Port": 1900, + "NotifyMulticastIntervalSeconds": 600, + "NotifyTTL": 5, + "NotifyIPv6Scope": "Site" + }, + "Telnet": { + "ProtocolEnabled": true, + "Port": 23 + }, + "KVMIP": { + "ProtocolEnabled": true, + "Port": 5288 + }, + "Oem": { } +} diff --git a/rsd_lib/tests/unit/resources/v2_1/manager/test_manager.py b/rsd_lib/tests/unit/resources/v2_1/manager/test_manager.py index 02572b9..0b85915 100644 --- a/rsd_lib/tests/unit/resources/v2_1/manager/test_manager.py +++ b/rsd_lib/tests/unit/resources/v2_1/manager/test_manager.py @@ -17,6 +17,7 @@ import mock from sushy.tests.unit import base from rsd_lib.resources.v2_1.manager import manager +from rsd_lib.resources.v2_1.manager import network_protocol class TestManager(base.TestCase): @@ -74,9 +75,6 @@ class TestManager(base.TestCase): ['Telnet', 'SSH'], self.manager_inst.command_shell.connect_types_supported) self.assertEqual('1.00', self.manager_inst.firmware_version) - self.assertEqual( - '/redfish/v1/Managers/PSME/NetworkProtocol', - self.manager_inst.network_protocol) self.assertEqual( '/redfish/v1/Managers/PSME/EthernetInterfaces', self.manager_inst.ethernet_interfaces) @@ -86,6 +84,57 @@ class TestManager(base.TestCase): self.manager_inst.links.manager_for_chassis) self.assertEqual('On', self.manager_inst.power_state) + def test__get_network_protocol_path(self): + expected = '/redfish/v1/Managers/PSME/NetworkProtocol' + result = self.manager_inst._get_network_protocol_path() + self.assertEqual(expected, result) + + def test_network_protocol(self): + # | GIVEN | + self.conn.get.return_value.json.reset_mock() + with open('rsd_lib/tests/unit/json_samples/v2_1/' + 'network_protocol.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN | + actual_network_protocol = self.manager_inst.network_protocol + # | THEN | + self.assertIsInstance(actual_network_protocol, + network_protocol.NetworkProtocol) + self.conn.get.return_value.json.assert_called_once_with() + + # reset mock + self.conn.get.return_value.json.reset_mock() + # | WHEN & THEN | + # tests for same object on invoking subsequently + self.assertIs(actual_network_protocol, + self.manager_inst.network_protocol) + self.conn.get.return_value.json.assert_not_called() + + def test_network_protocol_on_refresh(self): + # | GIVEN | + with open('rsd_lib/tests/unit/json_samples/v2_1/' + 'network_protocol.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.manager_inst.network_protocol, + network_protocol.NetworkProtocol) + + # On refreshing the manager instance... + with open('rsd_lib/tests/unit/json_samples/v2_1/' + 'manager.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.manager_inst.invalidate() + self.manager_inst.refresh(force=False) + + # | GIVEN | + with open('rsd_lib/tests/unit/json_samples/v2_1/' + 'network_protocol.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.manager_inst.network_protocol, + network_protocol.NetworkProtocol) + class TestManagerCollection(base.TestCase): @@ -106,7 +155,7 @@ class TestManagerCollection(base.TestCase): self.assertEqual('1.0.2', self.manager_col.redfish_version) self.assertEqual('Manager Collection', self.manager_col.name) self.assertEqual(('/redfish/v1/Managers/RMC', - '/redfish/v1/Managers/MBPC1', + '/redfish/v1/Managers/MBPC1', '/redfish/v1/Managers/MBPC2',), self.manager_col.members_identities) diff --git a/rsd_lib/tests/unit/resources/v2_1/manager/test_network_protocol.py b/rsd_lib/tests/unit/resources/v2_1/manager/test_network_protocol.py new file mode 100644 index 0000000..bf53b78 --- /dev/null +++ b/rsd_lib/tests/unit/resources/v2_1/manager/test_network_protocol.py @@ -0,0 +1,106 @@ +# 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. + +import json +import mock +import testtools + + +from rsd_lib.resources.v2_1.manager import network_protocol + + +class NetworkProtocolTestCase(testtools.TestCase): + + def setUp(self): + super(NetworkProtocolTestCase, self).setUp() + self.conn = mock.Mock() + with open( + 'rsd_lib/tests/unit/json_samples/v2_1/' + 'network_protocol.json', + 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.network_protocol_inst = network_protocol.NetworkProtocol( + self.conn, 'redfish/v1/Managers/BMC1/NetworkProtocol', + redfish_version='1.1.0') + + def test_parse_attributes(self): + self.network_protocol_inst._parse_attributes() + self.assertEqual('NetworkProtocol', self.network_protocol_inst. + identify) + self.assertEqual( + 'Manager Network Protocol', + self.network_protocol_inst.name) + self.assertEqual('Manager Network Service Status', + self.network_protocol_inst.description) + self.assertEqual('mymanager', self.network_protocol_inst.hostname) + self.assertEqual( + 'mymanager.mydomain.com', + self.network_protocol_inst.fqdn) + + # Status section + self.assertEqual('Enabled', + self.network_protocol_inst.status.state) + self.assertEqual('OK', + self.network_protocol_inst.status.health) + self.assertEqual(None, + self.network_protocol_inst.status.health_rollup) + + self.assertEqual(True, + self.network_protocol_inst.http.protocol_enables) + self.assertEqual(80, + self.network_protocol_inst.http.port) + + self.assertEqual(True, + self.network_protocol_inst.https.protocol_enables) + self.assertEqual(443, self.network_protocol_inst.https.port) + + self.assertEqual(True, + self.network_protocol_inst.ipmi.protocol_enables) + self.assertEqual(623, self.network_protocol_inst.ipmi.port) + + self.assertEqual(True, + self.network_protocol_inst.ssh.protocol_enables) + self.assertEqual(22, self.network_protocol_inst.ssh.port) + + self.assertEqual(True, + self.network_protocol_inst.snmp.protocol_enables) + self.assertEqual(161, + self.network_protocol_inst.snmp.port) + + self.assertEqual(True, + self.network_protocol_inst. + virtual_media.protocol_enables) + self.assertEqual(17988, + self.network_protocol_inst.virtual_media.port) + + self.assertEqual(True, + self.network_protocol_inst.ssdp.protocol_enables) + self.assertEqual(1900, + self.network_protocol_inst.ssdp.port) + self.assertEqual(600, + self.network_protocol_inst. + ssdp.notify_multicast_interval_seconds) + self.assertEqual(5, self.network_protocol_inst.ssdp.notify_ttl) + self.assertEqual('Site', self.network_protocol_inst. + ssdp.notify_ipv6_scope) + + self.assertEqual(True, self.network_protocol_inst. + telnet.protocol_enables) + self.assertEqual(23, self.network_protocol_inst.telnet.port) + + self.assertEqual(True, self.network_protocol_inst.kvm_ip. + protocol_enables) + self.assertEqual(5288, self.network_protocol_inst.kvm_ip.port)