diff --git a/rsd_lib/resources/v2_1/ethernet_switch/ethernet_switch_port.py b/rsd_lib/resources/v2_1/ethernet_switch/ethernet_switch_port.py index 441def2..27342c2 100644 --- a/rsd_lib/resources/v2_1/ethernet_switch/ethernet_switch_port.py +++ b/rsd_lib/resources/v2_1/ethernet_switch/ethernet_switch_port.py @@ -15,12 +15,14 @@ import logging +from jsonschema import validate from sushy.resources import base from sushy import utils from rsd_lib import base as rsd_lib_base from rsd_lib.resources.v2_1.common import ip_addresses from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch_static_mac +from rsd_lib.resources.v2_1.ethernet_switch import schemas as port_schema from rsd_lib.resources.v2_1.ethernet_switch import vlan_network_interface from rsd_lib import utils as rsd_lib_utils @@ -172,9 +174,32 @@ class EthernetSwitchPort(rsd_lib_base.ResourceBase): redfish_version=self.redfish_version, ) + def update(self, data=None): + """Update a new Port + + :param data: JSON for Port + """ + update_schema = port_schema.port_req_schema + del update_schema["required"] + if data is not None or len(data) > 0: + validate(data, update_schema) + self._conn.patch(self.path, data=data) + class EthernetSwitchPortCollection(rsd_lib_base.ResourceCollectionBase): @property def _resource_type(self): return EthernetSwitchPort + + def create_port(self, port_req): + """Create a new Port + + :param Port: JSON for Port + :returns: The location of the Port + """ + validate(port_req, port_schema.port_req_schema) + resp = self._conn.post(self._path, data=port_req) + port_url = resp.headers["Location"] + LOG.info("Create Port at %s", port_url) + return port_url[port_url.find(self._path):] diff --git a/rsd_lib/resources/v2_1/ethernet_switch/schemas.py b/rsd_lib/resources/v2_1/ethernet_switch/schemas.py index 6ced703..4c39f75 100644 --- a/rsd_lib/resources/v2_1/ethernet_switch/schemas.py +++ b/rsd_lib/resources/v2_1/ethernet_switch/schemas.py @@ -140,3 +140,176 @@ acl_rule_req_schema = { "required": ["Action", "Condition"], "additionalProperties": False, } + +port_req_schema = { + "type": "object", + "properties": { + "PortId": {"type": "number"}, + "LinkType": { + "type": "string", + "enum": ["Ethernet", "PCIe"], + }, + "OperationalState": { + "type": "string", + "enum": ["Up", "Down"], + }, + "AdministrativeState": { + "type": "string", + "enum": ["Up", "Down"], + }, + "LinkSpeedMbps": {"type": ["number", "null"]}, + "NeighborInfo": { + "type": "object", + "properties": { + "SwitchId": {"type": "string"}, + "PortId": {"type": "string"}, + "CableId": {"type": "string"}, + }, + "additionalProperties": False, + }, + "NeighborMAC": {"type": "string"}, + "FrameSize": {"type": ["number", "null"]}, + "Autosense": {"type": "boolean"}, + "FullDuplex": {"type": "boolean"}, + "MACAddress": {"type": "string"}, + "IPv4Addresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "Address": {"type": "string"}, + "SubnetMask": {"type": "string"}, + "AddressOrigin": { + "type": "string", + "enum": ["Static", "DHCP", "BOOTP", "IPv4LinkLocal"], + }, + "Gateway": {"type": "string"}, + }, + "additionalProperties": False, + }, + }, + "IPv6Addresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "Address": {"type": "string"}, + "PrefixLength": { + "type": "number", + "minimum": 1, + "maximum": 128, + }, + "AddressOrigin": { + "type": "string", + "enum": ["Static", "DHCPv6", "LinkLocal", "SLAAC"], + }, + "AddressState": { + "type": "string", + "enum": [ + "Preferred", + "Deprecated", + "Tentative", + "Failed", + ], + }, + }, + "additionalProperties": False, + }, + }, + "PortClass": { + "type": "string", + "enum": ["Physical", "Logical", "Reserved"], + }, + "PortMode": { + "type": "string", + "enum": [ + "LinkAggregationStatic", + "LinkAggregationDynamic", + "Unknown", + ], + }, + "PortType": { + "type": "string", + "enum": ["Upstream", "Downstream", "MeshPort", "Unknown"], + }, + "Status": { + "type": "object", + "properties": { + "State": { + "type": "string", + "enum": [ + "Enabled", + "Disabled", + "StandbyOffline", + "StandbySpare", + "InTest", + "Starting", + "Absent", + "UnavailableOffline", + "Deferring", + "Quiesced", + "Updating", + ], + }, + "HealthRollup": { + "type": "string", + "enum": ["OK", "Warning", "Critical"], + }, + "Health": { + "type": "string", + "enum": ["OK", "Warning", "Critical"], + }, + }, + "additionalProperties": False, + }, + "VLANs": { + "type": "object", + "properties": {"@odata.id": {"type": "string"}}, + "required": ["@odata.id"], + }, + "StaticMACs": { + "type": "object", + "properties": {"@odata.id": {"type": "string"}}, + "required": ["@odata.id"], + }, + "Links": { + "type": "object", + "properties": { + "PrimaryVLAN": { + "type": "object", + "properties": {"@odata.id": {"type": "string"}}, + "required": ["@odata.id"], + }, + "Switch": { + "type": "object", + "properties": {"@odata.id": {"type": "string"}}, + "required": ["@odata.id"], + }, + "MemberOfPort": { + "type": "object", + "properties": {"@odata.id": {"type": "string"}}, + "required": ["@odata.id"], + }, + "PortMembers": { + "type": "array", + "items": { + "type": "object", + "properties": {"@odata.id": {"type": "string"}}, + "required": ["@odata.id"], + }, + }, + "ActiveACLs": { + "type": "array", + "items": { + "type": "object", + "properties": {"@odata.id": {"type": "string"}}, + "required": ["@odata.id"], + }, + }, + }, + "additionalProperties": False, + }, + }, + "required": ["PortId"], + "additionalProperties": False, +} diff --git a/rsd_lib/tests/unit/resources/v2_1/ethernet_switch/test_ethernet_switch_port.py b/rsd_lib/tests/unit/resources/v2_1/ethernet_switch/test_ethernet_switch_port.py index 80f5bf3..7947c41 100644 --- a/rsd_lib/tests/unit/resources/v2_1/ethernet_switch/test_ethernet_switch_port.py +++ b/rsd_lib/tests/unit/resources/v2_1/ethernet_switch/test_ethernet_switch_port.py @@ -21,6 +21,7 @@ import testtools from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch_port from rsd_lib.resources.v2_1.ethernet_switch import ethernet_switch_static_mac from rsd_lib.resources.v2_1.ethernet_switch import vlan_network_interface +from rsd_lib.tests.unit.fakes import request_fakes class EthernetSwitchPortTestCase(testtools.TestCase): @@ -239,6 +240,31 @@ class EthernetSwitchPortTestCase(testtools.TestCase): vlan_network_interface.VLanNetworkInterfaceCollection, ) + def test_update(self): + data = { + "AdministrativeState": "Up", + "LinkSpeedMbps": 1000, + "FrameSize": 1500, + "Autosense": False, + "Links": { + "PrimaryVLAN": { + "@odata.id": "/redfish/v1/EthernetSwitches/" + "Switch1/Ports/Port11/VLans/VLan1" + }, + "PortMembers": [ + {"@odata.id": "/redfish/v1/EthernetSwitches/" + "Switch1/Ports/Port10"}, + {"@odata.id": "/redfish/v1/EthernetSwitches/" + "Switch1/Ports/Port12"}, + ], + }, + } + self.port_inst.update(data) + self.port_inst._conn.patch.assert_called_once_with( + "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1", + data=data, + ) + class EthernetSwitchPortCollectionTestCase(testtools.TestCase): def setUp(self): @@ -250,9 +276,16 @@ class EthernetSwitchPortCollectionTestCase(testtools.TestCase): "r", ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) + self.conn.post.return_value = request_fakes.fake_request_post( + None, + headers={ + "Location": "https://localhost:8443/redfish/v1/" + "EthernetSwitches/Switch1/Ports/Port1" + }, + ) self.port_col = ethernet_switch_port.EthernetSwitchPortCollection( self.conn, - "/redfish/v1/EthernetSwitches/Ports", + "/redfish/v1/EthernetSwitches/Switch1/Ports", redfish_version="1.0.2", ) @@ -290,3 +323,25 @@ class EthernetSwitchPortCollectionTestCase(testtools.TestCase): ) self.assertIsInstance(members, list) self.assertEqual(1, len(members)) + + def test_create_port_reqs(self): + reqs = { + "PortId": 1, + "PortMode": "LinkAggregationStatic", + "Links": { + "PortMembers": [ + {"@odata.id": "/redfish/v1/EthernetSwitches/" + "Switch1/Ports/Port10"}, + {"@odata.id": "/redfish/v1/EthernetSwitches/" + "Switch1/Ports/Port11"}, + ], + }, + } + result = self.port_col.create_port(reqs) + self.port_col._conn.post.assert_called_once_with( + "/redfish/v1/EthernetSwitches/Switch1/Ports", data=reqs + ) + self.assertEqual( + result, + "/redfish/v1/EthernetSwitches/Switch1/Ports/Port1", + )