add ethernet switch port vlan network interface for rsd 2.1

Change-Id: I1aaa5bac32808e9381295256570f7c7e387ccaa0
This commit is contained in:
ya.wang 2018-08-08 15:04:47 +08:00
parent 9c721a1ffa
commit 6b7aedfd06
6 changed files with 272 additions and 4 deletions

View File

@ -20,6 +20,7 @@ from sushy.resources import base
from sushy import utils
from rsd_lib.resources.v2_1.ethernet_switch import static_mac
from rsd_lib.resources.v2_1.ethernet_switch import vlan
from rsd_lib import utils as rsd_lib_utils
LOG = logging.getLogger(__name__)
@ -143,7 +144,7 @@ class Port(base.ResourceBase):
port_type = base.Field('PortType')
"""The port type"""
vlans = base.Field('VLANs', adapter=rsd_lib_utils.get_resource_identity)
_vlans = None # ref to VLANCollection instance
"""The port vlans"""
_static_macs = None # ref to StaticMACCollection instance
@ -184,9 +185,32 @@ class Port(base.ResourceBase):
return self._static_macs
def _get_vlan_collection_path(self):
"""Helper function to find the VLANCollection path"""
vlan_col = self.json.get('VLANs')
if not vlan_col:
raise exceptions.MissingAttributeError(attribute='VLAN',
resource=self._path)
return rsd_lib_utils.get_resource_identity(vlan_col)
@property
def vlans(self):
"""Property to provide reference to `VLANCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
if self._vlans is None:
self._vlans = vlan.VLANCollection(
self._conn, self._get_vlan_collection_path(),
redfish_version=self.redfish_version)
return self._vlans
def refresh(self):
super(Port, self).refresh()
self._static_macs = None
self._vlans = None
class PortCollection(base.ResourceCollectionBase):

View File

@ -0,0 +1,67 @@
# 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.
from sushy.resources import base
from rsd_lib import utils as rsd_lib_utils
class VLAN(base.ResourceBase):
identity = base.Field('Id', required=True)
"""The vlan network interface identity"""
name = base.Field('Name')
"""The vlan network interface name"""
description = base.Field('Description')
"""The vlan network interface description"""
vlan_enable = base.Field('VLANEnable', adapter=bool)
"""The boolean indicate this vlan network interface is enabled or not"""
vlan_id = base.Field('VLANId', adapter=rsd_lib_utils.int_or_none)
"""The vlan network interafce id"""
oem = base.Field('Oem')
"""The vlan network interface oem info"""
def __init__(self, connector, identity, redfish_version=None):
"""A class representing an VLAN network interface
:param connector: A Connector instance
:param identity: The identity of the VLAN network interface resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(VLAN, self).__init__(connector, identity, redfish_version)
class VLANCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return VLAN
def __init__(self, connector, path, redfish_version=None):
"""A class representing an VLAN network interface collection
:param connector: A Connector instance
:param path: The canonical path to the VLAN network interface
collection resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(VLANCollection, self).__init__(connector, path, redfish_version)

View File

@ -0,0 +1,20 @@
{
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1",
"@odata.context": "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface",
"@odata.type": "#VLanNetworkInterface.v1_0_0.VLanNetworkInterface",
"Id": "VLAN1",
"Name": "VLAN Network Interface",
"Description": "System NIC 1 VLAN",
"VLANEnable": true,
"VLANId": 101,
"Oem": {
"Intel_RackScale": {
"@odata.type": "#Intel.Oem.VLanNetworkInterface",
"Tagged": false,
"Status": {
"State": "Enabled",
"Health": "OK"
}
}
}
}

View File

@ -0,0 +1,13 @@
{
"@odata.context": "/redfish/v1/$metadata#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection",
"@odata.id": "/redfish/v1/EthernetSwitches",
"@odata.type": "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection",
"Name": "VLAN Network Interface Collection",
"Description": "VLAN Network Interface Collection",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1"
}
]
}

View File

@ -21,6 +21,7 @@ import testtools
from rsd_lib.resources.v2_1.ethernet_switch import port
from rsd_lib.resources.v2_1.ethernet_switch import static_mac
from rsd_lib.resources.v2_1.ethernet_switch import vlan
class PortTestCase(testtools.TestCase):
@ -80,9 +81,7 @@ class PortTestCase(testtools.TestCase):
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.assertIsNone(self.port_inst._vlans)
self.assertIsNone(self.port_inst._static_macs)
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1',
@ -156,6 +155,63 @@ class PortTestCase(testtools.TestCase):
self.assertIsInstance(self.port_inst.static_macs,
static_mac.StaticMACCollection)
def test__get_vlan_collection_path(self):
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs',
self.port_inst._get_vlan_collection_path())
def test__get_vlan_collection_path_missing_attr(self):
self.port_inst._json.pop('VLANs')
self.assertRaisesRegex(
exceptions.MissingAttributeError, 'attribute VLAN',
self.port_inst._get_vlan_collection_path)
def test_vlan(self):
# checkou for the underpath variable value
self.assertIsNone(self.port_inst._vlans)
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_vlan_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_vlans = self.port_inst.vlans
# | THEN |
self.assertIsInstance(actual_vlans,
vlan.VLANCollection)
self.conn.get.return_value.json.assert_called_once_with()
# reset mock
self.conn.get.return_value.json.reset_mock()
# | WHEN & THEN |
self.assertIs(actual_vlans, self.port_inst.vlans)
self.conn.get.return_value.json.assert_not_called()
def test_vlan_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_vlan_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.port_inst.vlans,
vlan.VLANCollection)
# On refreshing...
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.refresh()
# | WHEN & THEN |
self.assertIsNone(self.port_inst._vlans)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_vlan_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.port_inst.vlans, vlan.VLANCollection)
class PortCollectionTestCase(testtools.TestCase):

View File

@ -0,0 +1,88 @@
# 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 vlan
class VLANTestCase(testtools.TestCase):
def setUp(self):
super(VLANTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_vlan.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.vlan_inst = vlan.VLAN(
self.conn,
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.vlan_inst._parse_attributes()
self.assertEqual('1.0.2', self.vlan_inst.redfish_version)
self.assertEqual('VLAN1', self.vlan_inst.identity)
self.assertEqual('VLAN Network Interface', self.vlan_inst.name)
self.assertEqual('System NIC 1 VLAN', self.vlan_inst.description)
self.assertEqual(True, self.vlan_inst.vlan_enable)
self.assertEqual(101, self.vlan_inst.vlan_id)
class VLANCollectionTestCase(testtools.TestCase):
def setUp(self):
super(VLANCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_port_vlan_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.vlan_col = vlan.VLANCollection(
self.conn,
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs',
redfish_version='1.1.0')
def test__parse_attributes(self):
self.vlan_col._parse_attributes()
self.assertEqual('1.1.0', self.vlan_col.redfish_version)
self.assertEqual(
('/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1',),
self.vlan_col.members_identities)
@mock.patch.object(vlan, 'VLAN', autospec=True)
def test_get_member(self, mock_vlan):
self.vlan_col.get_member(
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1')
mock_vlan.assert_called_once_with(
self.vlan_col._conn,
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/VLANs/VLAN1',
redfish_version=self.vlan_col.redfish_version)
@mock.patch.object(vlan, 'VLAN', autopspec=True)
def test_get_members(self, mock_vlan):
members = self.vlan_col.get_members()
calls = [
mock.call(self.vlan_col._conn,
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port1/'
'VLANs/VLAN1',
redfish_version=self.vlan_col.redfish_version)
]
mock_vlan.assert_has_calls(calls)
self.assertIsInstance(members, list)
self.assertEqual(1, len(members))