Add acl rule field for rsd v2_1

Change-Id: I2e7bfaa0fdb8b5de4147900052438ac8a5a1b3ae
This commit is contained in:
ya.wang 2018-08-30 19:58:09 +08:00
parent 5db9a878e7
commit c00a55915e
6 changed files with 385 additions and 4 deletions

View File

@ -13,8 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
from sushy import exceptions
from sushy.resources import base
from rsd_lib.resources.v2_1.ethernet_switch import acl_rule
from rsd_lib import utils as rsd_lib_utils
@ -32,7 +34,7 @@ class ACL(base.ResourceBase):
oem = base.Field('Oem')
"""The acl oem info"""
rules = base.Field('Rules', adapter=rsd_lib_utils.get_resource_identity)
_rules = None # ref to ACLRuleCollection instance
"""The acl rules"""
links = base.Field('Links')
@ -48,6 +50,31 @@ class ACL(base.ResourceBase):
"""
super(ACL, self).__init__(connector, identity, redfish_version)
def _get_acl_rule_collection_path(self):
"""Helper function to find the RuleCollection path"""
rule_col = self.json.get('Rules')
if not rule_col:
raise exceptions.MissingAttributeError(attribute='ACLRules',
resource=self._path)
return rsd_lib_utils.get_resource_identity(rule_col)
@property
def rules(self):
"""Property to provide reference to `RuleCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
if self._rules is None:
self._rules = acl_rule.ACLRuleCollection(
self._conn, self._get_acl_rule_collection_path(),
redfish_version=self.redfish_version)
return self._rules
def refresh(self):
super(ACL, self).refresh()
self._rules = None
class ACLCollection(base.ResourceCollectionBase):

View File

@ -0,0 +1,119 @@
# 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 sushy import utils
from rsd_lib import utils as rsd_lib_utils
class IPSourceField(base.CompositeField):
ipv4_address = base.Field('IPv4Address')
mask = base.Field('Mask')
class MACSourceField(base.CompositeField):
address = base.Field('Address')
mask = base.Field('Mask', adapter=rsd_lib_utils.int_or_none)
class VLANIdField(base.CompositeField):
id = base.Field('Id', adapter=rsd_lib_utils.int_or_none)
mask = base.Field('Mask', adapter=rsd_lib_utils.int_or_none)
class L4SourcePortField(base.CompositeField):
port = base.Field('Port', adapter=rsd_lib_utils.int_or_none)
mask = base.Field('Mask', adapter=rsd_lib_utils.int_or_none)
class ConditionField(base.CompositeField):
ip_source = IPSourceField('IPSource')
ip_destination = base.Field('IPDestination')
mac_source = MACSourceField('MACSource')
mac_destination = base.Field('MACDestination')
vlan_id = VLANIdField('VLANId')
l4_source_port = L4SourcePortField('L4SourcePort')
l4_destination_port = base.Field('L4DestinationPort',
adapter=rsd_lib_utils.int_or_none)
l4_protocol = base.Field('L4Protocol')
class ACLRule(base.ResourceBase):
identity = base.Field('Id')
"""The acl rule identity string"""
name = base.Field('Name')
"""The acl rule name"""
description = base.Field('Description')
"""The acl rule description"""
rule_id = base.Field('RuleId', adapter=rsd_lib_utils.int_or_none)
"""The acl rule id"""
action = base.Field('Action')
"""The acl rule action"""
forward_mirror_interface = base.Field(
'ForwardMirrorInterface',
adapter=rsd_lib_utils.get_resource_identity)
"""The acl rule forward mirror interface"""
mirror_port_region = base.Field('MirrorPortRegion',
adapter=utils.get_members_identities)
"""The acl rule mirror port region"""
mirror_type = base.Field('MirrorType')
"""The acl rule mirror type"""
condition = ConditionField('Condition')
"""The acl rule condition field"""
oem = base.Field('Oem')
"""The ac rule oem field"""
links = base.Field('Links')
"""The acl rule links field"""
def __init__(self, connector, identity, redfish_version=None):
"""A class representing an ACL Rule
:param connector: A connector instance
:param identity: The identity of the ACL Rule resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(ACLRule, self).__init__(connector, identity, redfish_version)
class ACLRuleCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return ACLRule
def __init__(self, connector, path, redfish_version=None):
"""A class representing an ACL Rule Collection
:param connector: A Connector instance
:param path: The canonical path to the ACL Rule collection resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(ACLRuleCollection, self).__init__(connector,
path,
redfish_version)

View File

@ -0,0 +1,46 @@
{
"@odata.context": "/redfish/v1/$metadata#EthernetSwitches/Members/Switch1/ACLs/Members/Rules/Members/$entity",
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/Rules/Rule1",
"@odata.type": "#EthernetSwitchACLRule.v1_0_0.EthernetSwitchACLRule",
"Id": "Rule1",
"Name": "Example Rule",
"Description": "User defined rule for ACL",
"RuleId": 1,
"Action": "Mirror",
"ForwardMirrorInterface": {
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port9"
},
"MirrorPortRegion": [
{
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1"
},
{
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/Ports/Port2"
}
],
"MirrorType": "Bidirectional",
"Condition": {
"IPSource": {
"IPv4Address": "192.168.1.0",
"Mask": "0.0.0.255"
},
"IPDestination": null,
"MACSource": {
"Address": "00:11:22:33:44:55",
"Mask": null
},
"MACDestination": null,
"VLANId": {
"Id": 1088,
"Mask": 4095
},
"L4SourcePort": {
"Port": 22,
"Mask": 255
},
"L4DestinationPort": null,
"L4Protocol": null
},
"Oem": {},
"Links": {}
}

View File

@ -0,0 +1,13 @@
{
"@odata.context": "/redfish/v1/$metadata#EthernetSwitches/Members/Switch1/ACLs/Members/Rules",
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/Rules",
"@odata.type": "#EthernetSwitchACLRuleCollection.EthernetSwitchACLRuleCollection",
"Name": "Ethernet Switch Access Control List Rules Collection",
"Description": "Rules for switch Access Control List. Each Rule defines single action and at least one condition",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/Rules/Rule1"
}
]
}

View File

@ -18,7 +18,10 @@ import json
import mock
import testtools
from sushy import exceptions
from rsd_lib.resources.v2_1.ethernet_switch import acl
from rsd_lib.resources.v2_1.ethernet_switch import acl_rule
class ACLTestCase(testtools.TestCase):
@ -36,7 +39,7 @@ class ACLTestCase(testtools.TestCase):
redfish_version='1.0.2'
)
def test__parst_attributes(self):
def test__parse_attributes(self):
self.acl_inst._parse_attributes()
self.assertEqual('1.0.2', self.acl_inst.redfish_version)
self.assertEqual('ACL1', self.acl_inst.identity)
@ -44,8 +47,67 @@ class ACLTestCase(testtools.TestCase):
self.acl_inst.name)
self.assertEqual('Switch ACL', self.acl_inst.description)
self.assertEqual({}, self.acl_inst.oem)
self.assertEqual('/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/'
'Rules', self.acl_inst.rules)
self.assertIsNone(self.acl_inst._rules)
def test__get_acl_rule_collection_path(self):
self.assertEqual(
'/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/Rules',
self.acl_inst._get_acl_rule_collection_path())
def test__get_acl_rule_collection_path_missing_attr(self):
self.acl_inst._json.pop('Rules')
self.assertRaisesRegex(
exceptions.MissingAttributeError, 'attribute ACLRule',
self.acl_inst._get_acl_rule_collection_path)
def test_acl_rule(self):
# check for the underpath variable value
self.assertIsNone(self.acl_inst._rules)
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_acl.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_acl_rules = self.acl_inst.rules
# | THEN |
self.assertIsInstance(actual_acl_rules,
acl_rule.ACLRuleCollection)
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_acl_rules,
self.acl_inst.rules)
self.conn.get.return_value.json.assert_not_called()
def test_acl_rule_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'acl_rule_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.acl_inst.rules,
acl_rule.ACLRuleCollection)
# On refreshing the acl_rule instance...
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'ethernet_switch_acl.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.acl_inst.refresh()
# | WHEN & THEN |
self.assertIsNone(self.acl_inst._rules)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'acl_rule_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.acl_inst.rules,
acl_rule.ACLRuleCollection)
class ACLCollectionTestCase(testtools.TestCase):

View File

@ -0,0 +1,114 @@
# 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 acl_rule
class ACLRuleTestCase(testtools.TestCase):
def setUp(self):
super(ACLRuleTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/acl_rule.json',
'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.acl_rule_inst = acl_rule.ACLRule(
self.conn,
'/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/Rules/Rule1',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.acl_rule_inst._parse_attributes()
self.assertEqual('1.0.2', self.acl_rule_inst.redfish_version)
self.assertEqual('Rule1', self.acl_rule_inst.identity)
self.assertEqual('Example Rule', self.acl_rule_inst.name)
self.assertEqual('User defined rule for ACL',
self.acl_rule_inst.description)
self.assertEqual(1, self.acl_rule_inst.rule_id)
self.assertEqual('Mirror', self.acl_rule_inst.action)
self.assertEqual('/redfish/v1/EthernetSwitches/Switch1/Ports/Port9',
self.acl_rule_inst.forward_mirror_interface)
self.assertEqual(('/redfish/v1/EthernetSwitches/Switch1/Ports/Port1',
'/redfish/v1/EthernetSwitches/Switch1/Ports/Port2',),
self.acl_rule_inst.mirror_port_region)
self.assertEqual('Bidirectional',
self.acl_rule_inst.mirror_type)
self.assertEqual('192.168.1.0',
self.acl_rule_inst.condition.ip_source.ipv4_address)
self.assertEqual('0.0.0.255',
self.acl_rule_inst.condition.ip_source.mask)
self.assertEqual(None, self.acl_rule_inst.condition.ip_destination)
self.assertEqual('00:11:22:33:44:55',
self.acl_rule_inst.condition.mac_source.address)
self.assertEqual(None, self.acl_rule_inst.condition.mac_source.mask)
self.assertEqual(1088, self.acl_rule_inst.condition.vlan_id.id)
self.assertEqual(4095,
self.acl_rule_inst.condition.vlan_id.mask)
self.assertEqual(22,
self.acl_rule_inst.condition.l4_source_port.port)
self.assertEqual(255,
self.acl_rule_inst.condition.l4_source_port.mask)
self.assertEqual(None,
self.acl_rule_inst.condition.l4_destination_port)
class ACLRuleCollectionTestCase(testtools.TestCase):
def setUp(self):
super(ACLRuleCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_1/'
'acl_rule_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.acl_rule_col = acl_rule.ACLRuleCollection(
self.conn,
'/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/Rules',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.acl_rule_col._parse_attributes()
self.assertEqual('1.0.2', self.acl_rule_col.redfish_version)
self.assertEqual('Ethernet Switch Access Control '
'List Rules Collection',
self.acl_rule_col.name)
self.assertEqual(('/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/'
'Rules/Rule1',),
self.acl_rule_col.members_identities)
@mock.patch.object(acl_rule, 'ACLRule', autospec=True)
def test_get_member(self, mock_acl_rule):
self.acl_rule_col.get_member(
'/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/Rules/Rule1')
mock_acl_rule.assert_called_once_with(
self.acl_rule_col._conn,
'/redfish/v1/EthernetSwitches/Switch1/ACLs/ACL1/Rules/Rule1',
redfish_version=self.acl_rule_col.redfish_version
)
@mock.patch.object(acl_rule, 'ACLRule', autospec=True)
def test_get_members(self, mock_acl_rule):
members = self.acl_rule_col.get_members()
self.assertEqual(mock_acl_rule.call_count, 1)
self.assertIsInstance(members, list)
self.assertEqual(1, len(members))