From 4d14573ab95140cbd2ba63dd68cb009c25b5fa7d Mon Sep 17 00:00:00 2001 From: Tina Tang Date: Wed, 21 Sep 2016 17:03:46 +0800 Subject: [PATCH] [GH-22] Unity ethernet port modification support Support modifying the speed and mtu of the ethernet ports on unity system --- storops/exception.py | 8 + storops/lib/resource.py | 4 +- storops/unity/resource/port.py | 40 +++- test-requirements.txt | 3 +- test/unity/resource/test_port.py | 79 +++++++- test/unity/rest_data/ethernetPort/index.json | 44 +++++ .../rest_data/ethernetPort/spa_eth3.json | 59 ++++++ .../rest_data/ethernetPort/spb_eth3.json | 59 ++++++ .../unity/rest_data/ethernetPort/success.json | 0 test/unity/rest_data/ethernetPort/type.json | 177 ++++++++++++++++++ 10 files changed, 467 insertions(+), 6 deletions(-) create mode 100755 test/unity/rest_data/ethernetPort/index.json create mode 100755 test/unity/rest_data/ethernetPort/spa_eth3.json create mode 100755 test/unity/rest_data/ethernetPort/spb_eth3.json create mode 100755 test/unity/rest_data/ethernetPort/success.json create mode 100755 test/unity/rest_data/ethernetPort/type.json diff --git a/storops/exception.py b/storops/exception.py index 08d8c7e7..75080fc6 100644 --- a/storops/exception.py +++ b/storops/exception.py @@ -391,6 +391,14 @@ class UnityFileSystemSizeTooSmallError(UnityException): error_code = 108008449 +class UnityEthernetPortMtuSizeNotSupportError(UnityException): + message = "Specified MTU size is not supported." + + +class UnityEthernetPortSpeedNotSupportError(UnityException): + message = "Specified Speed is not supported." + + class UnityShareTypeNotSupportAccessControlError(UnityException): message = 'share type does not support access control.' diff --git a/storops/lib/resource.py b/storops/lib/resource.py index 4dcbcecc..cc62205d 100644 --- a/storops/lib/resource.py +++ b/storops/lib/resource.py @@ -158,7 +158,9 @@ def _get_properties(self, dec=0): elif isinstance(value, (datetime, timedelta)): value = str(value) elif isinstance(value, (tuple, list, set)): - value = [v.get_dict_repr(dec - 1) for v in value] + value = [v.get_dict_repr(dec - 1) + if isinstance(v, JsonPrinter) else v + for v in value] props[name] = value except AttributeError: # skip not available attributes diff --git a/storops/unity/resource/port.py b/storops/unity/resource/port.py index c891104a..dceb634e 100644 --- a/storops/unity/resource/port.py +++ b/storops/unity/resource/port.py @@ -18,6 +18,9 @@ import logging from storops.unity.resource import UnityResource, UnityResourceList +from storops.exception import UnityEthernetPortMtuSizeNotSupportError +from storops.exception import UnityEthernetPortSpeedNotSupportError +from storops.unity.enums import EPSpeedValuesEnum __author__ = 'Jay Xu' @@ -85,7 +88,42 @@ def get_resource_class(cls): class UnityEthernetPort(UnityResource): - pass + def modify(self, speed=None, mtu=None): + speed = EPSpeedValuesEnum.parse(speed) + peer = self.get_peer() + self._modify(self, speed, mtu) + self._modify(peer, speed, mtu) + + def get_peer(self): + _peer_id = self._get_peer_id(self.get_id()) + return UnityEthernetPort(cli=self._cli, _id=_peer_id) + + def _modify(self, port, speed, mtu): + if speed is not None: + if speed not in self.supported_speeds: + raise UnityEthernetPortSpeedNotSupportError + if speed == port.requested_speed: + speed = None + + if mtu is not None: + if mtu not in self.supported_mtus: + raise UnityEthernetPortMtuSizeNotSupportError + if mtu == port.requested_mtu: + mtu = None + + if speed is None and mtu is None: + return + + resp = self._cli.modify(self.resource_class, + port.get_id(), + speed=speed, mtuSize=mtu) + resp.raise_if_err() + + @staticmethod + def _get_peer_id(_id): + if _id.startswith('spa'): + return _id.replace('spa', 'spb') + return _id.replace('spb', 'spa') class UnityEthernetPortList(UnityResourceList): diff --git a/test-requirements.txt b/test-requirements.txt index 4ec6d44e..bcbbe37f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,4 +8,5 @@ pytest-xdist>=1.13.1 pytest-capturelog>=0.7 xmltodict>=0.9.2 cobertura-clover-transform>=1.1.4 -fasteners>=0.12.0 \ No newline at end of file +fasteners>=0.12.0 +ddt>=1.0.1 # MIT \ No newline at end of file diff --git a/test/unity/resource/test_port.py b/test/unity/resource/test_port.py index cf260a7e..b9e30907 100644 --- a/test/unity/resource/test_port.py +++ b/test/unity/resource/test_port.py @@ -17,9 +17,13 @@ from unittest import TestCase -from hamcrest import assert_that, equal_to, instance_of - -from storops.unity.resource.port import UnityIpPort, UnityIpPortList +import ddt +from hamcrest import assert_that, equal_to, instance_of, only_contains, raises +from storops.exception import UnityEthernetPortSpeedNotSupportError, \ + UnityEthernetPortMtuSizeNotSupportError +from storops.unity.enums import ConnectorTypeEnum, EPSpeedValuesEnum +from storops.unity.resource.port import UnityEthernetPort, UnityIpPort, \ + UnityIpPortList from storops.unity.resource.sp import UnityStorageProcessor from test.unity.rest_mock import t_rest, patch_rest @@ -40,3 +44,72 @@ def test_get_properties(self): def test_get_all(self): ports = UnityIpPortList(cli=t_rest()) assert_that(len(ports), equal_to(8)) + + +@ddt.ddt +class UnityEthernetPortTest(TestCase): + @patch_rest + def test_get_properties(self): + port = UnityEthernetPort('spa_eth3', cli=t_rest()) + assert_that(port.name, equal_to('SP A Ethernet Port 3')) + assert_that(port.mac_address, equal_to("00:60:16:5C:07:0A")) + assert_that(port.parent_storage_processor, equal_to( + UnityStorageProcessor('spa', cli=t_rest()))) + assert_that(port.mtu, equal_to(1500)) + assert_that(port.requested_mtu, equal_to(1500)) + assert_that(port.connector_type, equal_to(ConnectorTypeEnum.RJ45)) + assert_that(port.supported_speeds, only_contains( + EPSpeedValuesEnum.AUTO, + EPSpeedValuesEnum._100MbPS, + EPSpeedValuesEnum._1GbPS, + EPSpeedValuesEnum._10GbPS)) + assert_that(port.supported_mtus, only_contains(1500, 9000)) + assert_that(port.speed, equal_to(None)) + assert_that(port.needs_replacement, equal_to(False)) + assert_that(port.is_link_up, equal_to(False)) + assert_that(port.bond, equal_to(False)) + + @patch_rest + def test_modify_mtu(self): + port = UnityEthernetPort(cli=t_rest(), _id='spa_eth3') + port.modify(mtu=9000) + + @patch_rest + def test_modify_mtu_to_invalid_value(self): + def do(): + port = UnityEthernetPort(cli=t_rest(), _id='spb_eth3') + port.modify(mtu=10000) + + assert_that(do, raises(UnityEthernetPortMtuSizeNotSupportError)) + + @patch_rest + def test_modify_mtu_to_same_value(self): + port = UnityEthernetPort(cli=t_rest(), _id='spb_eth3') + port.modify(mtu=1500) + + @patch_rest + def test_modify_speed(self): + port = UnityEthernetPort(cli=t_rest(), _id='spa_eth3') + port.modify(speed=100) + + @patch_rest + def test_modify_speed_to_same_value(self): + port = UnityEthernetPort(cli=t_rest(), _id='spb_eth3') + port.modify(speed=EPSpeedValuesEnum.AUTO) + + @patch_rest + def test_modify_speed_to_invalid_value(self): + def do(): + port = UnityEthernetPort(cli=t_rest(), _id='spb_eth3') + port.modify(speed=40000) + assert_that(do, raises(UnityEthernetPortSpeedNotSupportError)) + + @ddt.data({'port_id': 'spa_eth2', + 'peer_id': 'spb_eth2'}, + {'port_id': 'spb_eth3', + 'peer_id': 'spa_eth3'}) + @ddt.unpack + def test_get_peer(self, port_id, peer_id): + port = UnityEthernetPort(cli=t_rest(), _id=port_id) + peer = port.get_peer() + assert_that(peer.get_id(), equal_to(peer_id)) diff --git a/test/unity/rest_data/ethernetPort/index.json b/test/unity/rest_data/ethernetPort/index.json new file mode 100755 index 00000000..ec510a25 --- /dev/null +++ b/test/unity/rest_data/ethernetPort/index.json @@ -0,0 +1,44 @@ +{ + "indices": [ + { + "url": "/api/types/ethernetPort?compact=True&fields=attributes.description,attributes.displayValue,attributes.initialValue,attributes.name,attributes.type,description,documentation,name,type", + "response": "type.json" + }, + { + "url": "/api/instances/ethernetPort/spa_eth3?compact=True&fields=bond,cascadeNames,connectorType,health,id,instanceId,isLinkUp,isRDMACapable,isRSSCapable,linuxDeviceName,macAddress,mtu,name,needsReplacement,operationalStatus,parent,parentIOModule,parentStorageProcessor,portNumber,requestedMtu,requestedSpeed,sfpSupportedProtocols,sfpSupportedSpeeds,shortName,speed,storageProcessor,supportedMtus,supportedSpeeds", + "response": "spa_eth3.json" + }, + { + "url": "/api/instances/ethernetPort/spb_eth3?compact=True&fields=bond,cascadeNames,connectorType,health,id,instanceId,isLinkUp,isRDMACapable,isRSSCapable,linuxDeviceName,macAddress,mtu,name,needsReplacement,operationalStatus,parent,parentIOModule,parentStorageProcessor,portNumber,requestedMtu,requestedSpeed,sfpSupportedProtocols,sfpSupportedSpeeds,shortName,speed,storageProcessor,supportedMtus,supportedSpeeds", + "response": "spb_eth3.json" + }, + { + "url": "/api/instances/ethernetPort/spa_eth3/action/modify?compact=True", + "body": { + "mtuSize": 9000 + }, + "response": "success.json" + }, + { + "url": "/api/instances/ethernetPort/spb_eth3/action/modify?compact=True", + "body": { + "mtuSize": 9000 + }, + "response": "success.json" + }, + { + "url": "/api/instances/ethernetPort/spb_eth3/action/modify?compact=True", + "body": { + "speed": 100 + }, + "response": "success.json" + }, + { + "url": "/api/instances/ethernetPort/spa_eth3/action/modify?compact=True", + "body": { + "speed": 100 + }, + "response": "success.json" + } + ] +} diff --git a/test/unity/rest_data/ethernetPort/spa_eth3.json b/test/unity/rest_data/ethernetPort/spa_eth3.json new file mode 100755 index 00000000..f6efc7d3 --- /dev/null +++ b/test/unity/rest_data/ethernetPort/spa_eth3.json @@ -0,0 +1,59 @@ +{ + "content": { + "macAddress": "00:60:16:5C:07:0A", + "isRDMACapable": false, + "parentStorageProcessor": { + "id": "spa" + }, + "instanceId": "root/emc:EMC_UEM_EthernetPortLeaf%Tag=03:00:00:01", + "portNumber": 3, + "cascadeNames": [ + "Ethernet Port 3", + "SP A" + ], + "requestedMtu": 1500, + "mtu": 1500, + "connectorType": 1, + "id": "spa_eth3", + "supportedSpeeds": [ + 1000, + 10000, + 100, + 0 + ], + "supportedMtus": [ + 1500, + 9000 + ], + "storageProcessor": { + "id": "spa" + }, + "health": { + "descriptionIds": [ + "ALRT_PORT_LINK_DOWN_NOT_IN_USE" + ], + "descriptions": [ + "The port link is down, but not in use. No action is required." + ], + "value": 5 + }, + "parent": { + "resource": "storageProcessor", + "id": "spa" + }, + "needsReplacement": false, + "sfpSupportedSpeeds": [], + "sfpSupportedProtocols": [], + "operationalStatus": [ + 2, + 32785 + ], + "shortName": "Ethernet Port 3", + "requestedSpeed": 0, + "isRSSCapable": false, + "name": "SP A Ethernet Port 3", + "linuxDeviceName": "eth3", + "isLinkUp": false, + "bond": false + } +} diff --git a/test/unity/rest_data/ethernetPort/spb_eth3.json b/test/unity/rest_data/ethernetPort/spb_eth3.json new file mode 100755 index 00000000..55027f76 --- /dev/null +++ b/test/unity/rest_data/ethernetPort/spb_eth3.json @@ -0,0 +1,59 @@ +{ + "content": { + "macAddress": "00:60:16:5C:05:FE", + "isRDMACapable": false, + "parentStorageProcessor": { + "id": "spb" + }, + "instanceId": "root/emc:EMC_UEM_EthernetPortLeaf%Tag=03:00:01:01", + "portNumber": 3, + "cascadeNames": [ + "Ethernet Port 3", + "SP B" + ], + "requestedMtu": 1500, + "mtu": 1500, + "connectorType": 1, + "id": "spb_eth3", + "supportedSpeeds": [ + 1000, + 10000, + 100, + 0 + ], + "supportedMtus": [ + 1500, + 9000 + ], + "storageProcessor": { + "id": "spb" + }, + "health": { + "descriptionIds": [ + "ALRT_PORT_LINK_DOWN_NOT_IN_USE" + ], + "descriptions": [ + "The port link is down, but not in use. No action is required." + ], + "value": 5 + }, + "parent": { + "resource": "storageProcessor", + "id": "spb" + }, + "needsReplacement": false, + "sfpSupportedSpeeds": [], + "sfpSupportedProtocols": [], + "operationalStatus": [ + 2, + 32785 + ], + "shortName": "Ethernet Port 3", + "requestedSpeed": 0, + "isRSSCapable": false, + "name": "SP B Ethernet Port 3", + "linuxDeviceName": "eth3", + "isLinkUp": false, + "bond": false + } +} diff --git a/test/unity/rest_data/ethernetPort/success.json b/test/unity/rest_data/ethernetPort/success.json new file mode 100755 index 00000000..e69de29b diff --git a/test/unity/rest_data/ethernetPort/type.json b/test/unity/rest_data/ethernetPort/type.json new file mode 100755 index 00000000..e7ca101f --- /dev/null +++ b/test/unity/rest_data/ethernetPort/type.json @@ -0,0 +1,177 @@ +{ + "content": { + "attributes": [ + { + "displayValue": "instanceId", + "type": "String", + "name": "instanceId", + "description": "Internal identifier of the Ethernet port instance. " + }, + { + "displayValue": "id", + "type": "String", + "name": "id", + "description": "Unique identifier of the Ethernet port instance. " + }, + { + "displayValue": "operationalStatus", + "type": "List", + "name": "operationalStatus", + "description": "Current operational status information for the Ethernet port. " + }, + { + "displayValue": "health", + "type": "health", + "name": "health", + "description": "Health information for the Ethernet port, as defined by the health resource type. " + }, + { + "displayValue": "storageProcessor", + "type": "storageProcessor", + "name": "storageProcessor", + "description": "SP on which the Ethernet port directly or indirectly resides, as defined by the storageProcessor resource type. " + }, + { + "displayValue": "needsReplacement", + "type": "Boolean", + "name": "needsReplacement", + "description": "Indicates whether the Ethernet port needs replacement. Values are: " + }, + { + "displayValue": "name", + "type": "String", + "name": "name", + "description": "User-specified Ethernet port name. " + }, + { + "displayValue": "portNumber", + "type": "Integer", + "name": "portNumber", + "description": "Physical Ethernet port sequence number. " + }, + { + "displayValue": "speed", + "type": "EPSpeedValuesEnum", + "name": "speed", + "description": "Current link speed of the Ethernet port. " + }, + { + "displayValue": "mtu", + "type": "Integer", + "name": "mtu", + "description": "Maximum Transmission Unit (MTU) packet size that the Ethernet port can transmit. The default is 1500 bytes per packet. " + }, + { + "displayValue": "connectorType", + "type": "ConnectorTypeEnum", + "name": "connectorType", + "description": "Physical connector type. " + }, + { + "displayValue": "bond", + "type": "Boolean", + "name": "bond", + "description": "Indicates whether the Ethernet port is used in a link aggregation. Values are: " + }, + { + "displayValue": "isLinkUp", + "type": "Boolean", + "name": "isLinkUp", + "description": "Indicates whether the Ethernet port has link. (Applies if the Ethernet port is configured with a link.) Indicates whether the Ethernet port's link is up. Values are: