Add port field of ethernet switch for rsd v2_1

- Add port field of ethernet switch for rsd v2_1
- Fix some error of ethernet switch for rsd v2_1

Change-Id: I09fe3a06762b5325e5460aec61884341f43c8995
This commit is contained in:
ya.wang 2018-06-20 13:14:38 +08:00
parent 517275b24f
commit 66c8336e9a
7 changed files with 527 additions and 21 deletions

View File

@ -15,9 +15,11 @@
import logging
from sushy import exceptions
from sushy.resources import base
from sushy import utils
from rsd_lib.resources.v2_1.ethernet_switch import port
from rsd_lib import utils as rsd_lib_utils
LOG = logging.getLogger(__name__)
@ -58,8 +60,8 @@ class EthernetSwitch(base.ResourceBase):
model = base.Field('Model')
"""The ethernet switch model"""
manufacturing_data = base.Field('ManufacturingData')
"""The ethernet switch manufacturing data"""
manufacturing_date = base.Field('ManufacturingDate')
"""The ethernet switch manufacturing date"""
seria_number = base.Field('SerialNumber')
"""The ethernet switch serial number"""
@ -83,8 +85,7 @@ class EthernetSwitch(base.ResourceBase):
adapter=rsd_lib_utils.get_resource_identity)
"""The ethernet switch ACLs"""
ports = base.Field('Ports', default=(),
adapter=rsd_lib_utils.get_resource_identity)
_ports = None # ref to PortCollection instance
"""The ethernet switch ports"""
links = LinksField('Links')
@ -102,6 +103,33 @@ class EthernetSwitch(base.ResourceBase):
identity,
redfish_version)
def _get_port_collection_path(self):
"""Helper functionto find the PortCollection path"""
port_col = self.json.get('Ports')
if not port_col:
raise exceptions.MissingAttributeError(attribute='Ports',
resource=self._path)
return rsd_lib_utils.get_resource_identity(port_col)
@property
def ports(self):
"""Property to provide reference to `PortCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
if self._ports is None:
self._ports = port.PortCollection(
self._conn, self._get_port_collection_path(),
redfish_version=self.redfish_version
)
return self._ports
def refresh(self):
super(EthernetSwitch, self).refresh()
self._ports = None
class EthernetSwitchCollection(base.ResourceCollectionBase):

View File

@ -0,0 +1,179 @@
# Copyright 2018 99cloud, 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
from sushy import utils
from rsd_lib import utils as rsd_lib_utils
LOG = logging.getLogger(__name__)
class StatusField(base.CompositeField):
state = base.Field('State')
"""The port state"""
health = base.Field('Health')
"""The port health"""
health_rollup = base.Field('HealthRollup')
"""The port health rollup"""
class NeighborInfoField(base.CompositeField):
switch_id = base.Field('SwitchId')
port_id = base.Field('PortId')
cable_id = base.Field('CableId')
class IPv4AddressesField(base.ListField):
address = base.Field('Address')
"""The port ipv4 address"""
subnet_mask = base.Field('SubnetMask')
"""The port ipv4 address subnet mask"""
address_origin = base.Field('AddressOrigin')
"""The port ipv4 address origin"""
gateway = base.Field('Gateway')
"""The port ipv4 address gateway"""
class IPv6AddressesField(base.ListField):
address = base.Field('Address')
"""The port ipv6 address"""
prefix_length = base.Field('PrefixLength', adapter=int)
"""The port ipv6 address prefix length"""
address_origin = base.Field('AddressOrigin')
"""The port ipv6 address origin"""
address_state = base.Field('AddressState')
"""The port ipv6 address gateway"""
class LinksField(base.CompositeField):
primary_vlan = base.Field('PrimaryVLAN',
adapter=rsd_lib_utils.get_resource_identity)
switch = base.Field('Switch', adapter=rsd_lib_utils.get_resource_identity)
member_of_port = base.Field('MemberOfPort',
adapter=rsd_lib_utils.get_resource_identity)
port_members = base.Field('PortMembers', adapter=list)
active_acls = base.Field('ActiveACLs',
adapter=utils.get_members_identities)
class Port(base.ResourceBase):
identity = base.Field('Id', required=True)
"""The port identity string"""
name = base.Field('Name')
"""The port name"""
description = base.Field('Description')
"""The port description"""
port_id = base.Field('PortId')
"""The port id"""
status = StatusField('Status')
"""The port status"""
link_type = base.Field('LinkType')
"""The port link type"""
operational_state = base.Field('OperationalState')
"""The port operational state"""
administrative_state = base.Field('AdministrativeState')
"""The port administrative state"""
link_speed_mbps = base.Field('LinkSpeedMbps', adapter=int)
"""The port link speed(mbps)"""
neighbor_info = NeighborInfoField('NeighborInfo')
"""The port neighbor info"""
neighbor_mac = base.Field('NeighborMAC')
"""The port neighbor mac"""
frame_size = base.Field('FrameSize', adapter=int)
"""The port frame size"""
autosense = base.Field('Autosense', adapter=bool)
"""The boolean indicate the autosense is enabled or not"""
full_duplex = base.Field('FullDuplex', adapter=bool)
"""The boolean indicate the full duplex is enabled or not"""
mac_address = base.Field('MACAddress')
"""The port mac address"""
ipv4_addresses = IPv4AddressesField('IPv4Addresses')
"""The port ipv4 link info"""
ipv6_addresses = IPv6AddressesField('IPv6Addresses')
"""The port ipv6 link info"""
port_class = base.Field('PortClass')
"""The port class"""
port_mode = base.Field('PortMode')
"""The port mode"""
port_type = base.Field('PortType')
"""The port type"""
vlans = base.Field('VLANs', adapter=rsd_lib_utils.get_resource_identity)
"""The port vlans"""
static_macs = base.Field('StaticMACs',
adapter=rsd_lib_utils.get_resource_identity)
"""The port static macs"""
links = LinksField('Links')
"""The port links"""
def __init__(self, connector, identity, redfish_version=None):
"""A class representing an Port
:param connector: A Connector instance
:param identity: The identity of the Port resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(Port, self).__init__(connector, identity, redfish_version)
class PortCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return Port
def __init__(self, connector, path, redfish_version=None):
"""A class representing an Port
:param connector: A Connector instance
:param path: The canonical path to the Port collection resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(PortCollection, self).__init__(connector, path, redfish_version)

View File

@ -0,0 +1,79 @@
{
"@odata.context": "/redfish/v1/$metadata#EthernetSwitches/Members/1/Ports/Members/1/$entity",
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1",
"@odata.type": "#EthernetSwitchPort.v1_0_0.EthernetSwitchPort",
"Id": "Port1",
"Name": "Switch Port",
"Description": "description-as-string",
"PortId": "sw0p10",
"Status": {
"State": "Enabled",
"Health": "OK",
"HealthRollup": "OK"
},
"LinkType": "Ethernet",
"OperationalState": "Up",
"AdministrativeState": "Up",
"LinkSpeedMbps": 10000,
"NeighborInfo": {
"SwitchId": "sw2",
"PortId": "11",
"CableId": "CustomerWritableThing"
},
"NeighborMAC": "00:11:22:33:44:55",
"FrameSize": 1520,
"Autosense": true,
"FullDuplex": true,
"MACAddress": "2c:60:0c:72:e6:33",
"IPv4Addresses": [
{
"Address": "192.168.0.10",
"SubnetMask": "255.255.252.0",
"AddressOrigin": "Static",
"Gateway": "192.168.0.1"
}
],
"IPv6Addresses": [
{
"Address": "fe80::1ec1:deff:fe6f:1e24",
"PrefixLength": 64,
"AddressOrigin": "Static",
"AddressState": "Preferred"
}
],
"PortClass": "Logical",
"PortMode": "LinkAggregationStatic",
"PortType": "Upstream",
"Oem": {},
"VLANs": {
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs"
},
"StaticMACs": {
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/StaticMACs"
},
"Links": {
"PrimaryVLAN": {
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1"
},
"Switch": {
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1"
},
"MemberOfPort": {
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/LAG1"
},
"PortMembers": [],
"ActiveACLs": [
{
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1"
}
],
"Oem": {
"Intel_RackScale": {
"@odata.type": "#Intel.Oem.EthernetSwitchPort",
"NeighborInterface": {
"@odata.id": "/redfish/v1/Systems/1/EthernetInterfaces/3"
}
}
}
}
}

View File

@ -0,0 +1,13 @@
{
"@odata.context": "/redfish/v1/$metadata#EthernetSwitches/Members/1/Ports",
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports",
"@odata.type": "#SwitchPortsCollection.SwitchPortsCollection",
"Name": "Ethernet Switch Port Collection",
"Description": "Switch Port Collection",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1"
}
]
}

View File

@ -1,3 +1,6 @@
# 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
@ -13,16 +16,17 @@
import json
import mock
from sushy.tests.unit import base
from sushy import exceptions
import testtools
from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch
from rsd_lib.resources.v2_1.ethernet_switch import port
class TestEthernetSwtich(base.TestCase):
class EthernetSwtichTestCase(testtools.TestCase):
def setUp(self):
super(TestEthernetSwtich, self).setUp()
super(EthernetSwtichTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/ethernet_switch.json',
@ -34,7 +38,7 @@ class TestEthernetSwtich(base.TestCase):
'/redfish/v1/EthernetSwitches/Switch1',
redfish_version='1.0.2')
def test_parse_attributes(self):
def test__parse_attributes(self):
self.ethernet_switch_inst._parse_attributes()
self.assertEqual('1.0.2', self.ethernet_switch_inst.redfish_version)
self.assertEqual('Switch1', self.ethernet_switch_inst.identity)
@ -44,7 +48,7 @@ class TestEthernetSwtich(base.TestCase):
self.assertEqual('Quanta', self.ethernet_switch_inst.manufacturer)
self.assertEqual('ly8_rangley', self.ethernet_switch_inst.model)
self.assertEqual('02/21/2015 00:00:00',
self.ethernet_switch_inst.manufacturing_data)
self.ethernet_switch_inst.manufacturing_date)
self.assertEqual('2M220100SL', self.ethernet_switch_inst.seria_number)
self.assertEqual('1LY8UZZ0007', self.ethernet_switch_inst.part_number)
self.assertEqual('ONIE', self.ethernet_switch_inst.firmware_name)
@ -54,22 +58,81 @@ class TestEthernetSwtich(base.TestCase):
self.assertEqual('OK', self.ethernet_switch_inst.status.health)
self.assertEqual('/redfish/v1/EthernetSwitches/Switch1/ACLs',
self.ethernet_switch_inst.acls)
self.assertEqual('/redfish/v1/EthernetSwitches/Switch1/Ports',
self.ethernet_switch_inst.ports)
self.assertIsNone(self.ethernet_switch_inst._ports)
self.assertEqual('/redfish/v1/Chassis/FabricModule1',
self.ethernet_switch_inst.links.chassis)
self.assertEqual(('/redfish/v1/EthernetSwitches/Switch1/Ports',),
self.assertEqual(('/redfish/v1/Managers/Manager1',),
self.ethernet_switch_inst.links.managed_by)
def test__get_port_collection_path(self):
expected = '/redfish/v1/EthernetSwitches/Switch1/Ports'
result = self.ethernet_switch_inst._get_port_collection_path()
self.assertEqual(expected, result)
class TestEthernetSwitchCollection(base.TestCase):
def test__get_port_collection_path_missing_attr(self):
self.ethernet_switch_inst._json.pop('Ports')
self.assertRaisesRegex(
exceptions.MissingAttributeError, 'attribute Ports',
self.ethernet_switch_inst._get_port_collection_path)
def test_ports(self):
# check for the underpath variable value
self.assertIsNone(self.ethernet_switch_inst._ports)
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_ports = self.ethernet_switch_inst.ports
# | THEN |
self.assertIsInstance(actual_ports,
port.PortCollection)
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_ports,
self.ethernet_switch_inst.ports)
self.conn.get.return_value.json.assert_not_called()
def test_ports_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.ethernet_switch_inst.ports,
port.PortCollection)
# On refreshing the port instance...
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.ethernet_switch_inst.refresh()
# | WHEN & THEN |
self.assertIsNone(self.ethernet_switch_inst._ports)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.ethernet_switch_inst.ports,
port.PortCollection)
class EthernetSwitchCollectionTestCase(testtools.TestCase):
def setUp(self):
super(TestEthernetSwitchCollection, self).setUp()
super(EthernetSwitchCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'manager_collection.json', 'r') as f:
'ethernet_switch_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.ethernet_switch_col = ethernet_switch.EthernetSwitchCollection(
@ -77,17 +140,18 @@ class TestEthernetSwitchCollection(base.TestCase):
'redfish/v1/EthernetSwitches',
redfish_version='1.0.2')
def test_parse_attributes(self):
def test__parse_attributes(self):
self.ethernet_switch_col._parse_attributes()
self.assertEqual('1.0.2', self.ethernet_switch_col.redfish_version)
self.assertEqual('Ethernet Switches Collection',
self.ethernet_switch_col.name)
self.assertIn(('/redfish/v1/EthernetSwitches/Switch1',),
self.ethernet_switch_col.members_identities)
self.assertEqual(('/redfish/v1/EthernetSwitches/Switch1',),
self.ethernet_switch_col.members_identities)
@mock.patch.object(ethernet_switch, 'EthernetSwitch', autospec=True)
def test_get_member(self, mock_ethernet_switch):
self.manager_col.get_member('/redfish/v1/EthernetSwitches/Switch1')
self.ethernet_switch_col.get_member(
'/redfish/v1/EthernetSwitches/Switch1')
mock_ethernet_switch.assert_called_once_with(
self.ethernet_switch_col._conn,
@ -99,5 +163,5 @@ class TestEthernetSwitchCollection(base.TestCase):
def test_get_members(self, mock_ethernet_switch):
members = self.ethernet_switch_col.get_members()
self.assertEqual(mock_ethernet_switch.call_count, 1)
self.assertInstance(members, list)
self.assertIsInstance(members, list)
self.assertEqual(1, len(members))

View File

@ -0,0 +1,143 @@
# Copyright 2018 99cloud, 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.ethernet_switch import port
class PortTestCase(testtools.TestCase):
def setUp(self):
super(PortTestCase, self).setUp()
self.conn = mock.Mock()
with open(
'rsd_lib/tests/unit/json_samples/v2_1/ethernet_switch_port.json',
'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.port_inst = port.Port(
self.conn, '/redfish/v1/EthernetSwitches/Switch1/Ports/Port1',
redfish_version='1.0.2'
)
def test__parse_attributes(self):
self.port_inst._parse_attributes()
self.assertEqual('1.0.2', self.port_inst.redfish_version)
self.assertEqual('Port1', self.port_inst.identity)
self.assertEqual('Switch Port', self.port_inst.name)
self.assertEqual('description-as-string', self.port_inst.description)
self.assertEqual('sw0p10', self.port_inst.port_id)
self.assertEqual('Enabled', self.port_inst.status.state)
self.assertEqual('OK', self.port_inst.status.health)
self.assertEqual('OK', self.port_inst.status.health_rollup)
self.assertEqual('Ethernet', self.port_inst.link_type)
self.assertEqual('Up', self.port_inst.operational_state)
self.assertEqual('Up', self.port_inst.administrative_state)
self.assertEqual(10000, self.port_inst.link_speed_mbps)
self.assertEqual('sw2', self.port_inst.neighbor_info.switch_id)
self.assertEqual('11', self.port_inst.neighbor_info.port_id)
self.assertEqual('CustomerWritableThing',
self.port_inst.neighbor_info.cable_id)
self.assertEqual('00:11:22:33:44:55',
self.port_inst.neighbor_mac)
self.assertEqual(1520, self.port_inst.frame_size)
self.assertEqual(True, self.port_inst.autosense)
self.assertEqual(True, self.port_inst.full_duplex)
self.assertEqual('2c:60:0c:72:e6:33', self.port_inst.mac_address)
self.assertEqual('192.168.0.10',
self.port_inst.ipv4_addresses[0].address)
self.assertEqual('255.255.252.0',
self.port_inst.ipv4_addresses[0].subnet_mask)
self.assertEqual('Static',
self.port_inst.ipv4_addresses[0].address_origin)
self.assertEqual('192.168.0.1',
self.port_inst.ipv4_addresses[0].gateway)
self.assertEqual('fe80::1ec1:deff:fe6f:1e24',
self.port_inst.ipv6_addresses[0].address)
self.assertEqual(64, self.port_inst.ipv6_addresses[0].prefix_length)
self.assertEqual('Static',
self.port_inst.ipv6_addresses[0].address_origin)
self.assertEqual('Preferred',
self.port_inst.ipv6_addresses[0].address_state)
self.assertEqual('Logical', self.port_inst.port_class)
self.assertEqual('LinkAggregationStatic', self.port_inst.port_mode)
self.assertEqual('Upstream', self.port_inst.port_type)
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs',
self.port_inst.vlans)
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/StaticMACs',
self.port_inst.static_macs)
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1',
self.port_inst.links.primary_vlan)
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1',
self.port_inst.links.switch)
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1/Ports/LAG1',
self.port_inst.links.member_of_port)
self.assertEqual([], self.port_inst.links.port_members)
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1',
self.port_inst.links.active_acls[0])
class PortCollectionTestCase(testtools.TestCase):
def setUp(self):
super(PortCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open(
'rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.port_col = port.PortCollection(
self.conn, '/redfish/v1/EthernetSwitch/Ports',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.port_col._parse_attributes()
self.assertEqual('1.0.2', self.port_col.redfish_version)
self.assertEqual(
'Ethernet Switch Port Collection',
self.port_col.name)
self.assertEqual(
('/redfish/v1/EthernetSwitches/Switch1/Ports/Port1'),
self.port_col.members_identities)
@mock.patch.object(port, 'Port', autospec=True)
def test_get_member(self, mock_port):
self.port_col.get_member(
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1'
)
mock_port.assert_called_once_with(
self.port_col._conn,
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1',
redfish_version=self.port_col.redfish_version)
@mock.patch.object(port, 'Port', autospec=True)
def test_get_members(self, mock_port):
members = self.port_col.get_members()
mock_port.assert_called_with(
self.port_col.__conn,
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1',
redfish_version=self.port_col.redfish_version)
self.assertIsInstance(members, list)
self.assertEqual(1, len(members))