From 56cd986bbad50cd053fdf57004a419ae98c595af Mon Sep 17 00:00:00 2001 From: Balagangadhar Bathula Date: Wed, 19 Feb 2020 17:04:55 -0500 Subject: [PATCH] SH-Renderer API code to handle OTN Step 2 - improve ODU4 support - Fixing connection-type issue for roadmline JIRA: TRNSPRTPCE-194 Signed-off-by: Balagangadhar Bathula Change-Id: I78afa16ed4706b1e5f74eeec5259d56e58eae181 --- .../renderer/ModelMappingUtils.java | 44 ++++++++++----- .../OpenRoadmInterface221.java | 54 +++++++++++++++++++ .../OpenRoadmInterfaceFactory.java | 13 +++++ .../OpenRoadmOtnInterface221.java | 2 +- .../DeviceRendererServiceImpl.java | 22 ++++---- .../OtnDeviceRendererServiceImpl.java | 14 ++++- .../RendererServiceOperationsImpl.java | 17 +++--- .../2.2.1/test_otn_renderer.py | 53 ++++++++---------- 8 files changed, 151 insertions(+), 68 deletions(-) diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/ModelMappingUtils.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/ModelMappingUtils.java index 9ad44e48f..f2fec566b 100644 --- a/renderer/src/main/java/org/opendaylight/transportpce/renderer/ModelMappingUtils.java +++ b/renderer/src/main/java/org/opendaylight/transportpce/renderer/ModelMappingUtils.java @@ -119,11 +119,15 @@ public final class ModelMappingUtils { // If atoZ is set true use A-to-Z direction otherwise use Z-to-A List nodes = new ArrayList<>(); - NodeLists nodeLists = getNodesListAToZ(pathDescription.getAToZDirection().getAToZ().iterator()); - if (!asideToZside) { + NodeLists nodeLists = null; + if (asideToZside) { + nodeLists = getNodesListAToZ(pathDescription.getAToZDirection().getAToZ().iterator()); + } else { nodeLists = getNodesListZtoA(pathDescription.getZToADirection().getZToA().iterator()); } - for (Nodes node: nodeLists.getList()) { + LOG.info("These are node-lists {}, {}", nodeLists.getList(), nodeLists.getOlmList()); + for (int i = 0; i < nodeLists.getList().size(); i++) { + Nodes node = nodeLists.getList().get(i); if (serviceRate.equals("100G")) { nodes.add( new org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev200615.otn.renderer.input @@ -133,13 +137,25 @@ public final class ModelMappingUtils { .build()); } else { // For any other service rate (1G or 10G) - nodes.add( - new org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev200615.otn.renderer.input - .NodesBuilder() - .setNodeId(node.getNodeId()) - .setClientTp(node.getSrcTp()) - .setNetworkTp(node.getDestTp()) - .build()); + // For the last node in the list, clientTp and NetworkTp has to be reversed + if (i == nodeLists.getList().size() - 1) { + nodes.add( + new org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev200615.otn.renderer.input + .NodesBuilder() + .setNodeId(node.getNodeId()) + .setClientTp(node.getDestTp()) + .setNetworkTp(node.getSrcTp()) + .build()); + + } else { + nodes.add( + new org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev200615.otn.renderer.input + .NodesBuilder() + .setNodeId(node.getNodeId()) + .setClientTp(node.getSrcTp()) + .setNetworkTp(node.getDestTp()) + .build()); + } } } OtnServicePathInputBuilder otnServicePathInputBuilder = new OtnServicePathInputBuilder() @@ -203,7 +219,7 @@ public final class ModelMappingUtils { } else if ("Link".equals(resourceType)) { LOG.info("The type is link"); } else { - LOG.info("The type is not indentified: {}", resourceType); + LOG.info("The type is not identified: {}", resourceType); } } catch (IllegalArgumentException | SecurityException e) { LOG.error("Dont find the getResource method", e); @@ -230,7 +246,7 @@ public final class ModelMappingUtils { try { if (TERMINATION_POINT.equals(resourceType)) { tp = (TerminationPoint) pathDesObj.getResource().getResource(); - LOG.info(" TP is {} {}", tp.getTpId(), + LOG.info("TP is {} {}", tp.getTpId(), tp.getTpNodeId()); tpID = tp.getTpId(); nodeID = tp.getTpNodeId(); @@ -256,11 +272,11 @@ public final class ModelMappingUtils { } else if ("Link".equals(resourceType)) { LOG.info("The type is link"); } else { - LOG.info("The type is not indentified: {}", resourceType); + LOG.info("The type is not identified: {}", resourceType); } } catch (IllegalArgumentException | SecurityException e) { //TODO: Auto-generated catch block - LOG.error("Dont find the getResource method", e); + LOG.error("Did not find the getResource method", e); } } populateNodeLists(treeMap, list, olmList); diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmInterface221.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmInterface221.java index 5c0656cef..e6b7f4ad2 100644 --- a/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmInterface221.java +++ b/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmInterface221.java @@ -570,4 +570,58 @@ public class OpenRoadmInterface221 { return oduInterfaceBldr.getName(); } + public String createOpenRoadmOtnOdu4Interface(String anodeId, String alogicalConnPoint, + String asupportingOtuInterface, String znodeId, String zlogicalConnPoint) + throws OpenRoadmInterfaceException { + Mapping portMapA = portMapping.getMapping(anodeId, alogicalConnPoint); + if (portMapA == null) { + throw new OpenRoadmInterfaceException( + String.format("Unable to get mapping from PortMapping for node % and logical connection port %s", + anodeId, alogicalConnPoint)); + } + Mapping portMapZ = portMapping.getMapping(znodeId, zlogicalConnPoint); + if (portMapZ == null) { + throw new OpenRoadmInterfaceException( + String.format("Unable to get mapping from PortMapping for node % and logical connection port %s", + znodeId, zlogicalConnPoint)); + } + InterfaceBuilder oduInterfaceBldr = createGenericInterfaceBuilder(portMapA, OtnOdu.class, + alogicalConnPoint + "-ODU4"); + oduInterfaceBldr.setSupportingInterface(asupportingOtuInterface); + + // ODU interface specific data + OduBuilder oduIfBuilder = new OduBuilder() + .setRate(ODU4.class) + .setMonitoringMode(OduAttributes.MonitoringMode.Terminated) + .setTxSapi(portMapA.getLcpHashVal()) + .setTxDapi(portMapZ.getLcpHashVal()) + .setExpectedSapi(portMapZ.getLcpHashVal()) + .setExpectedDapi(portMapA.getLcpHashVal()); + + + // Set Opu attributes + OpuBuilder opuBldr = new OpuBuilder() + .setPayloadType(PayloadTypeDef.getDefaultInstance("21")) + .setExpPayloadType(PayloadTypeDef.getDefaultInstance("21")); + oduIfBuilder.setOduFunction(ODUTTP.class) + .setOpu(opuBldr.build()); + + // Create Interface1 type object required for adding as augmentation + // TODO look at imports of different versions of class + org.opendaylight.yang.gen.v1.http.org.openroadm.otn.odu.interfaces.rev181019.Interface1Builder oduIf1Builder = + new org.opendaylight.yang.gen.v1.http.org.openroadm.otn.odu.interfaces.rev181019.Interface1Builder(); + oduInterfaceBldr.addAugmentation( + org.opendaylight.yang.gen.v1.http.org.openroadm.otn.odu.interfaces.rev181019.Interface1.class, + oduIf1Builder.setOdu(oduIfBuilder.build()).build()); + + // Post interface on the device + openRoadmInterfaces.postInterface(anodeId, oduInterfaceBldr); + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + LOG.error("Error waiting post interface on device", e); + } + this.portMapping.updateMapping(anodeId, portMapA); + return oduInterfaceBldr.getName(); + } } diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmInterfaceFactory.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmInterfaceFactory.java index 9f85cc8d6..50319bbad 100644 --- a/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmInterfaceFactory.java +++ b/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmInterfaceFactory.java @@ -325,4 +325,17 @@ public class OpenRoadmInterfaceFactory { return null; } } + + public String createOpenRoadmOtnOdu4Interface(String anodeId, String alogicalConnPoint, + String asupportingOtuInterface, String znodeId, String zlogicalConnPoint) + throws OpenRoadmInterfaceException { + switch (mappingUtils.getOpenRoadmVersion(anodeId)) { + case StringConstants.OPENROADM_DEVICE_VERSION_2_2_1: + return openRoadmInterface221 + .createOpenRoadmOtnOdu4Interface(anodeId, alogicalConnPoint, asupportingOtuInterface, + znodeId, zlogicalConnPoint); + default: + return null; + } + } } diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmOtnInterface221.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmOtnInterface221.java index e6ad5ada3..495707145 100644 --- a/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmOtnInterface221.java +++ b/renderer/src/main/java/org/opendaylight/transportpce/renderer/openroadminterface/OpenRoadmOtnInterface221.java @@ -146,7 +146,7 @@ public class OpenRoadmOtnInterface221 { if (supportingInterface == null) { throw new OpenRoadmInterfaceException( "Interface Creation failed because of missing supported " - + "ODU4 on network end or Eth iface on client"); + + "ODU4 on network end or Ethernet on client"); } InterfaceBuilder oduInterfaceBldr = createGenericInterfaceBuilder( diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/DeviceRendererServiceImpl.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/DeviceRendererServiceImpl.java index 40a2a20ff..0f6c87a5b 100644 --- a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/DeviceRendererServiceImpl.java +++ b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/DeviceRendererServiceImpl.java @@ -98,11 +98,6 @@ public class DeviceRendererServiceImpl implements DeviceRendererService { @Override public ServicePathOutput setupServicePath(ServicePathInput input, ServicePathDirection direction) { List nodes = input.getNodes(); - Nodes srcNode = nodes.get(0); - // If the Node list size is one, then src and tgt are same; - // sapi/dapi all have the same value - Nodes tgtNode = nodes.get(nodes.size() - 1); - // Register node for suppressing alarms if (!alarmSuppressionNodeRegistration(input)) { @@ -115,8 +110,11 @@ public class DeviceRendererServiceImpl implements DeviceRendererService { ServiceListTopology topology = new ServiceListTopology(); AtomicBoolean success = new AtomicBoolean(true); ForkJoinPool forkJoinPool = new ForkJoinPool(); + ForkJoinTask forkJoinTask = forkJoinPool.submit(() -> nodes.parallelStream().forEach(node -> { String nodeId = node.getNodeId(); + // take the index of the node + int nodeIndex = nodes.indexOf(node); LOG.info("Starting provisioning for node : {}", nodeId); List createdEthInterfaces = new ArrayList<>(); List createdOtuInterfaces = new ArrayList<>(); @@ -136,15 +134,17 @@ public class DeviceRendererServiceImpl implements DeviceRendererService { nodeId, destTp, waveNumber, ModulationFormat.DpQpsk); createdOchInterfaces.add(supportingOchInterface); // Here we pass logical connection-point of z-end to set SAPI and DAPI - String znodeId = tgtNode.getNodeId(); - String zlogicalConnection = tgtNode.getDestTp(); - if (nodeId.equals(tgtNode.getNodeId())) { - znodeId = srcNode.getNodeId(); // if it is final node, then set zSide as source side - zlogicalConnection = srcNode.getDestTp(); + Nodes tgtNode = null; + if (nodeIndex + 1 == nodes.size()) { + // For the end node, tgtNode becomes the first node in the list + tgtNode = nodes.get(0); + } else { + tgtNode = nodes.get(nodeIndex + 1); } + // tgtNode srcTp is null in this if cond String supportingOtuInterface = this.openRoadmInterfaceFactory .createOpenRoadmOtu4Interface(nodeId, destTp, supportingOchInterface, - znodeId, zlogicalConnection); + tgtNode.getNodeId(), tgtNode.getDestTp()); createdOtuInterfaces.add(supportingOtuInterface); if (srcTp == null) { otnNodesProvisioned.add(node); diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/OtnDeviceRendererServiceImpl.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/OtnDeviceRendererServiceImpl.java index 800f76252..3328dc124 100644 --- a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/OtnDeviceRendererServiceImpl.java +++ b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/OtnDeviceRendererServiceImpl.java @@ -363,11 +363,21 @@ public class OtnDeviceRendererServiceImpl implements OtnDeviceRendererService { private void createODU4TtpInterface(OtnServicePathInput input, List nodeInterfaces, CopyOnWriteArrayList otnNodesProvisioned) throws OpenRoadmInterfaceException { - for (Nodes node : input.getNodes()) { + + for (int i = 0; i < input.getNodes().size(); i++) { + Nodes node = input.getNodes().get(i); String supportingOtuInterface = node.getNetworkTp() + "-OTU"; List createdOdu4Interfaces = new ArrayList<>(); + // Adding SAPI/DAPI information to the + Nodes tgtNode = null; + if (i + 1 == input.getNodes().size()) { + // For the end node, tgtNode becomes the first node in the list + tgtNode = input.getNodes().get(0); + } else { + tgtNode = input.getNodes().get(i + 1); + } createdOdu4Interfaces.add(openRoadmInterfaceFactory.createOpenRoadmOtnOdu4Interface(node.getNodeId(), - node.getNetworkTp(), supportingOtuInterface)); + node.getNetworkTp(), supportingOtuInterface, tgtNode.getNodeId(), tgtNode.getNetworkTp())); NodeInterfaceBuilder nodeInterfaceBuilder = new NodeInterfaceBuilder() .withKey(new NodeInterfaceKey(node.getNodeId())) .setNodeId(node.getNodeId()) diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/RendererServiceOperationsImpl.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/RendererServiceOperationsImpl.java index 4e38ea754..66bca1cf0 100644 --- a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/RendererServiceOperationsImpl.java +++ b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/RendererServiceOperationsImpl.java @@ -134,11 +134,12 @@ public class RendererServiceOperationsImpl implements RendererServiceOperations // Here is the switch statement that distinguishes on the connection-type LOG.info("Connection-type is {} for {}", input.getConnectionType(), input.getServiceName()); switch (input.getConnectionType()) { - case Service: // This takes into account of Ethernet 100G, 1G, 10G and ODU4 + case Service: case RoadmLine: // This takes into account of Ethernet 100G, 1G, 10G and ODU4 LOG.info("RPC implementation for {}", input.getConnectionType()); - if ((input.getServiceAEnd().getServiceRate() != null) // Since service-rate could be null - && (input.getServiceAEnd().getServiceRate().intValue() == 100) - && (input.getServiceAEnd().getServiceFormat().getName().equals("Ethernet"))) { + if (((input.getServiceAEnd().getServiceRate() != null) + && (input.getServiceAEnd().getServiceRate().intValue() == 100)) + && ((input.getServiceAEnd().getServiceFormat().getName().equals("Ethernet")) + || (input.getServiceAEnd().getServiceFormat().getName().equals("OC")))) { LOG.info("Service format for {} is {} and rate is {}", input.getServiceName(), input.getServiceAEnd().getServiceFormat(), input.getServiceAEnd().getServiceRate()); ServicePathInputData servicePathInputDataAtoZ = ModelMappingUtils @@ -200,8 +201,6 @@ public class RendererServiceOperationsImpl implements RendererServiceOperations } // If Service activation is success update Network ModelMappingUtils networkModelWavelengthService.useWavelengths(input.getPathDescription()); - sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, - input.getServiceName(), RpcStatusEx.Successful, OPERATION_SUCCESSFUL); } else { // This implies, service-rate is 1 or 10G // This includes the lower-order odu (1 G, 10 G) and LOG.info("RPC implementation for LO-ODU"); @@ -234,9 +233,9 @@ public class RendererServiceOperationsImpl implements RendererServiceOperations OPERATION_FAILED); } LOG.info("OTN rendering result size {}", otnRenderingResults.size()); - sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, - input.getServiceName(), RpcStatusEx.Successful, OPERATION_SUCCESSFUL); } + sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, + input.getServiceName(), RpcStatusEx.Successful, OPERATION_SUCCESSFUL); break; case Infrastructure: LOG.info("RPC implementation for {}", input.getConnectionType()); @@ -304,7 +303,7 @@ public class RendererServiceOperationsImpl implements RendererServiceOperations // First create the OCH and OTU interfaces String serviceRate = "100G"; // For OtnDeviceRendererServiceImpl LOG.info("Service format for {} is {} and rate is {}", input.getServiceName(), - input.getServiceAEnd().getOtuServiceRate(), serviceRate); + input.getServiceAEnd().getOduServiceRate(), serviceRate); // Now start rendering ODU4 interface String serviceFormat = "ODU"; // Since we need to create ODU4 Ttp interfaces as well // This is A-Z side diff --git a/tests/transportpce_tests/2.2.1/test_otn_renderer.py b/tests/transportpce_tests/2.2.1/test_otn_renderer.py index 48ca830c4..9c8abbc9b 100644 --- a/tests/transportpce_tests/2.2.1/test_otn_renderer.py +++ b/tests/transportpce_tests/2.2.1/test_otn_renderer.py @@ -146,25 +146,12 @@ class TransportPCEtesting(unittest.TestCase): self.assertEqual(response.status_code, requests.codes.ok) res = response.json() - input_dict = {'name': 'XPDR1-NETWORK1-1', - 'administrative-state': 'inService', - 'supporting-circuit-pack-name': 'CP1-CFP0', - 'type': 'org-openroadm-interfaces:opticalChannel', - 'supporting-port': 'CP1-CFP0-P1' - } - # assertDictContainsSubset is deprecated - ''' - self.assertDictContainsSubset({'name': 'XPDR1-NETWORK1-1', 'administrative-state': 'inService', - 'supporting-circuit-pack-name': 'CP1-CFP0', - 'type': 'org-openroadm-interfaces:opticalChannel', - 'supporting-port': 'CP1-CFP0-P1'}, res['interface'][0]) - ''' - self.assertDictEqual(dict({'name': 'XPDR1-NETWORK1-1', + self.assertDictEqual(dict(res['interface'][0], **{'name': 'XPDR1-NETWORK1-1', 'administrative-state': 'inService', 'supporting-circuit-pack-name': 'CP1-CFP0', 'type': 'org-openroadm-interfaces:opticalChannel', 'supporting-port': 'CP1-CFP0-P1' - } ,**res['interface'][0]), + } ), res['interface'][0]) self.assertDictEqual( @@ -197,7 +184,7 @@ class TransportPCEtesting(unittest.TestCase): 'fec': 'scfec' } - self.assertDictEqual(dict(input_dict_1, **res['interface'][0]), + self.assertDictEqual(dict(res['interface'][0], **input_dict_1), res['interface'][0]) self.assertDictEqual(input_dict_2, @@ -262,12 +249,16 @@ class TransportPCEtesting(unittest.TestCase): 'type': 'org-openroadm-interfaces:otnOdu', 'supporting-port': 'CP1-CFP0-P1'} input_dict_2 = {'odu-function': 'org-openroadm-otn-common-types:ODU-TTP', - 'rate': 'org-openroadm-otn-common-types:ODU4'} + 'rate': 'org-openroadm-otn-common-types:ODU4', + 'expected-dapi': 'Swfw02qXGyI=', + 'expected-sapi': 'Swfw02qXGyI=', + 'tx-dapi': 'Swfw02qXGyI=', + 'tx-sapi': 'Swfw02qXGyI='} - self.assertDictEqual(dict(input_dict_1, **res['interface'][0]), + self.assertDictEqual(dict(res['interface'][0], **input_dict_1), res['interface'][0]) - self.assertDictEqual(dict(input_dict_2, - **res['interface'][0]['org-openroadm-otn-odu-interfaces:odu'] + self.assertDictEqual(dict(res['interface'][0]['org-openroadm-otn-odu-interfaces:odu'], + **input_dict_2 ), res['interface'][0]['org-openroadm-otn-odu-interfaces:odu'] ) @@ -318,7 +309,7 @@ class TransportPCEtesting(unittest.TestCase): 'type': 'org-openroadm-interfaces:ethernetCsmacd', 'supporting-port': 'CP1-SFP4-P1' } - self.assertDictEqual(dict(input_dict, **res['interface'][0]), + self.assertDictEqual(dict(res['interface'][0], **input_dict), res['interface'][0]) self.assertDictEqual( {u'speed': 10000}, @@ -345,10 +336,10 @@ class TransportPCEtesting(unittest.TestCase): 'rate': 'org-openroadm-otn-common-types:ODU2e', 'monitoring-mode': 'terminated'} - self.assertDictEqual(dict(input_dict_1, **res['interface'][0]), + self.assertDictEqual(dict(res['interface'][0], **input_dict_1), res['interface'][0]) - self.assertDictEqual(dict(input_dict_2, - **res['interface'][0]['org-openroadm-otn-odu-interfaces:odu']), + self.assertDictEqual(dict(res['interface'][0]['org-openroadm-otn-odu-interfaces:odu'], + **input_dict_2), res['interface'][0]['org-openroadm-otn-odu-interfaces:odu']) self.assertDictEqual( {u'payload-type': u'03', u'exp-payload-type': u'03'}, @@ -375,14 +366,14 @@ class TransportPCEtesting(unittest.TestCase): input_dict_3 = {'trib-port-number': 1} - self.assertDictEqual(dict(input_dict_1, **res['interface'][0]), + self.assertDictEqual(dict(res['interface'][0], **input_dict_1), res['interface'][0]) - self.assertDictEqual(dict(input_dict_2, - **res['interface'][0]['org-openroadm-otn-odu-interfaces:odu']), + self.assertDictEqual(dict(res['interface'][0]['org-openroadm-otn-odu-interfaces:odu'], + **input_dict_2), res['interface'][0]['org-openroadm-otn-odu-interfaces:odu']) - self.assertDictEqual(dict(input_dict_3, - **res['interface'][0]['org-openroadm-otn-odu-interfaces:odu'][ - 'parent-odu-allocation']), + self.assertDictEqual(dict(res['interface'][0]['org-openroadm-otn-odu-interfaces:odu'][ + 'parent-odu-allocation'], **input_dict_3 + ), res['interface'][0]['org-openroadm-otn-odu-interfaces:odu'][ 'parent-odu-allocation']) self.assertIn(1, @@ -405,7 +396,7 @@ class TransportPCEtesting(unittest.TestCase): 'direction': 'bidirectional' } - self.assertDictEqual(dict(input_dict_1, **res['odu-connection'][0]), + self.assertDictEqual(dict(res['odu-connection'][0], **input_dict_1), res['odu-connection'][0]) self.assertDictEqual({u'dst-if': u'XPDR1-NETWORK1-ODU2e-service1'}, res['odu-connection'][0]['destination']) -- 2.36.6