From: Joakim Törnqvist Date: Thu, 7 Dec 2023 14:13:13 +0000 (+0000) Subject: Determine preferred node/port from PCRI X-Git-Tag: 9.0.0~43^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=transportpce.git;a=commitdiff_plain;h=0ff30a6562e1d6fd8aa31a7b795ca5ecb398d7b0 Determine preferred node/port from PCRI Package intended to be used to determine preferred SRG port usage when setting up optical services between ROADMs. Use an instance of PathComputationRequestInput, e.g. API input and provides a method for checking if a specific SRG port is among the ports in the client request. Package org.opendaylight.transportpce.pce.networkanalyzer.port: * Preference - Interface defining one method capable of determining if a node/port is 'preferred' by the client. * ClientPreference - Implements Preference. Contains information about actual client port preference. * NoPreference - Implements Preference. Typically used when the client has no preference regarding port. * Factory - Interface defining methods needed in order to convert a PathComputationRequestInput to an implementation of Preferenc. * PreferenceFactory - Implements Factory. The method 'portPreference' should typically be used implementing the feature in the application. The method portPreference either returns an instance of 'ClientPreference' or 'NoPreference'. Example: public void someMethod(PathComputationRequestInput input) { Factory portPreferenceFactory = new PreferenceFactory(); Preference preference = portPreferenceFactory.portPreference(input); if (preference.preferredPort("ROADM-A-SRG1", "SRG1-PP1-TXRX")) { System.out.println("The port 'SRG1-PP1-TXRX' on 'ROADM-A-SRG1' " +"is preferred by the client."); } } JIRA: TRNSPRTPCE-176 Change-Id: Iba7e7886678deb6d965391633f819995635caa06 Signed-off-by: Joakim Törnqvist --- diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/ClientPreference.java b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/ClientPreference.java new file mode 100644 index 000000000..d3749c382 --- /dev/null +++ b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/ClientPreference.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Smartoptics and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.transportpce.pce.networkanalyzer.port; + +import java.util.Map; +import java.util.Set; + +public class ClientPreference implements Preference { + + Map> nodePortPreference; + + public ClientPreference(Map> nodePortPreference) { + this.nodePortPreference = nodePortPreference; + } + + @Override + public boolean isPreferredPort(String node, String portName) { + + //If there is no preferred port registered for the node, it means + //the client has no preference regarding the node. + //Therefore, we'll treat the node as it was preferred to + //prevent it from NOT being used. + return !nodePortPreference.containsKey(node) || nodePortPreference.get(node).contains(portName); + + } + +} diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/Factory.java b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/Factory.java new file mode 100644 index 000000000..11721f520 --- /dev/null +++ b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/Factory.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Smartoptics and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.transportpce.pce.networkanalyzer.port; + +import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev230925.PathComputationRequestInput; + +public interface Factory { + + /** + * Extracting preferred ports from pathComputationRequestInput. + * + *

+ * This is the recommended method of determining if a node/port combination + * is preferred by the client. + * + *

+ * Pseudocode example: + *

+     *     Factory.portPreference(PCRI).preferredPort("ROADM-B-SRG1", "SRG1-PP1-TXRX");
+     * 
+ * + * @return Client port preference + */ + Preference portPreference(PathComputationRequestInput pathComputationRequestInput); + +} diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/NoPreference.java b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/NoPreference.java new file mode 100644 index 000000000..5b96ca09a --- /dev/null +++ b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/NoPreference.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Smartoptics and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.transportpce.pce.networkanalyzer.port; + +/** + * This class represents a state where the client has no port preference. + * In essence, all ports on all nodes will therefore be treated as 'preferred' + * when queried. + * + *

+ * Usage of this class is of sorts the 'backwards compatible' approach. Meaning, + * intended to offer a path for the application to behave as it did + * prior to implementing client port preference. + */ +public class NoPreference implements Preference { + @Override + public boolean isPreferredPort(String node, String portName) { + return true; + } +} diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/Preference.java b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/Preference.java new file mode 100644 index 000000000..3d1acb86d --- /dev/null +++ b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/Preference.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 Smartoptics and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.transportpce.pce.networkanalyzer.port; + +public interface Preference { + + /** + * Return true if the portName is among the ports preferred by the client. + */ + boolean isPreferredPort(String node, String portName); + +} diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/PreferenceFactory.java b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/PreferenceFactory.java new file mode 100644 index 000000000..6d36d8212 --- /dev/null +++ b/pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/port/PreferenceFactory.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2024 Smartoptics and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.transportpce.pce.networkanalyzer.port; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev230925.PathComputationRequestInput; +import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev230925.path.computation.request.input.ServiceAEnd; +import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev230925.path.computation.request.input.ServiceZEnd; +import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.port.Port; +import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.service.endpoint.sp.RxDirection; +import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.service.endpoint.sp.TxDirection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PreferenceFactory implements Factory { + + private static final Logger LOG = LoggerFactory.getLogger(PreferenceFactory.class); + + private final String portNamePattern = "(?i)SRG\\d+-PP\\d+-(TXRX|TX|RX)"; + + @Override + public Preference portPreference(PathComputationRequestInput pathComputationRequestInput) { + + Map> map = nodePortMap(pathComputationRequestInput); + + if (map.isEmpty()) { + LOG.debug("No port preference found in path computation request."); + return new NoPreference(); + } + + LOG.debug("Port preference in path computation request: {}." , map); + return new ClientPreference(map); + } + + /** + * Create a key value mapper from PCRI where key is the node and the value is + * a unique list of port names. + * + * @return Client port preference map + */ + Map> nodePortMap(PathComputationRequestInput pathComputationRequestInput) { + + Map> mapper = new HashMap<>(); + + ServiceAEnd serviceAEnd = pathComputationRequestInput.getServiceAEnd(); + if (serviceAEnd != null) { + + RxDirection rxAzDirection = serviceAEnd.getRxDirection(); + if (rxAzDirection != null) { + + Port rxAZport = rxAzDirection.getPort(); + if (rxAZport != null) { + add(rxAZport.getPortDeviceName(), rxAZport.getPortName(), mapper); + } + } + + TxDirection txAzDirection = serviceAEnd.getTxDirection(); + if (txAzDirection != null) { + + Port txAZport = txAzDirection.getPort(); + if (txAZport != null) { + add(txAZport.getPortDeviceName(), txAZport.getPortName(), mapper); + } + } + } + + ServiceZEnd serviceZEnd = pathComputationRequestInput.getServiceZEnd(); + if (serviceZEnd != null) { + + RxDirection rxZaDirection = serviceZEnd.getRxDirection(); + if (rxZaDirection != null) { + + Port rxZAport = rxZaDirection.getPort(); + if (rxZAport != null) { + add(rxZAport.getPortDeviceName(), rxZAport.getPortName(), mapper); + } + } + + TxDirection txZaDirection = serviceZEnd.getTxDirection(); + if (txZaDirection != null) { + + Port txZAport = txZaDirection.getPort(); + if (txZAport != null) { + add(txZAport.getPortDeviceName(), txZAport.getPortName(), mapper); + } + } + } + + return mapper; + } + + /** + * Add node/port name to key value map. Mutable method, modifies the argument nodePortMap. + */ + boolean add(String node, String port, Map> nodePortMap) { + + if (node == null || port == null) { + return false; + } + + String nodeTrimmed = node.trim(); + String portTrimmed = port.trim(); + + if (nodeTrimmed.isEmpty() || portTrimmed.isEmpty()) { + return false; + } + + if (!portTrimmed.matches(portNamePattern)) { + LOG.warn("Preferred port name '{}' on node {} doesn't match pattern '{}'", + portTrimmed, + nodeTrimmed, + portNamePattern + ); + } + + if (nodePortMap.containsKey(nodeTrimmed)) { + boolean added = nodePortMap.get(nodeTrimmed).add(portTrimmed); + if (added) { + LOG.debug("Preferred port '{}' for node '{}' registered.", portTrimmed, nodeTrimmed); + } else { + LOG.debug("Failed registering port '{}' for node '{}'.", portTrimmed, nodeTrimmed); + } + return added; + } + + nodePortMap.put(nodeTrimmed, new HashSet<>(Arrays.asList(portTrimmed))); + + return true; + } +} diff --git a/pce/src/test/java/org/opendaylight/transportpce/pce/networkanalyzer/port/ClientPreferenceTest.java b/pce/src/test/java/org/opendaylight/transportpce/pce/networkanalyzer/port/ClientPreferenceTest.java new file mode 100644 index 000000000..24662213b --- /dev/null +++ b/pce/src/test/java/org/opendaylight/transportpce/pce/networkanalyzer/port/ClientPreferenceTest.java @@ -0,0 +1,63 @@ +/* + * Copyright © 2024 Smartoptics and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.transportpce.pce.networkanalyzer.port; + + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class ClientPreferenceTest { + + @Test + void preferredPort_returnTrue() { + + Map> nodePortPreference = new HashMap<>(); + nodePortPreference.put("ROADM-B-SRG1", Set.of("SRG1-PP1-TXRX")); + + Preference clientPreference = new ClientPreference(nodePortPreference); + + Assertions.assertTrue(clientPreference.isPreferredPort("ROADM-B-SRG1", "SRG1-PP1-TXRX")); + } + + /** + * The client prefer to use SRG1-PP1-TXRX on ROADM-B-SRG1. + * Therefore, preferredPort returns false on SRG1-PP2-TXRX. + */ + @Test + void nonPreferredPort_returnFalse() { + + Map> nodePortPreference = new HashMap<>(); + nodePortPreference.put("ROADM-B-SRG1", Set.of("SRG1-PP1-TXRX")); + + Preference clientPreference = new ClientPreference(nodePortPreference); + + Assertions.assertFalse(clientPreference.isPreferredPort("ROADM-B-SRG1", "SRG1-PP2-TXRX")); + } + + /** + * In this scenario ROADM-A-SRG1 is missing from the client preferred list. + * We treat this as the client has no opinion on what port + * to use on ROADM-A-SRG1. Meaning, as far as the client goes, all + * ports on ROADM-A-SRG1 are fine. + */ + @Test + void nodeMissingInPreferredList_returnTrue() { + + Map> nodePortPreference = new HashMap<>(); + nodePortPreference.put("ROADM-B-SRG1", Set.of("SRG1-PP1-TXRX")); + + Preference clientPreference = new ClientPreference(nodePortPreference); + + Assertions.assertTrue(clientPreference.isPreferredPort("ROADM-A-SRG1", "SRG1-PP2-TXRX")); + + } +} \ No newline at end of file diff --git a/pce/src/test/java/org/opendaylight/transportpce/pce/networkanalyzer/port/PreferenceFactoryTest.java b/pce/src/test/java/org/opendaylight/transportpce/pce/networkanalyzer/port/PreferenceFactoryTest.java new file mode 100644 index 000000000..34ebb5cae --- /dev/null +++ b/pce/src/test/java/org/opendaylight/transportpce/pce/networkanalyzer/port/PreferenceFactoryTest.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2024 Smartoptics and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.transportpce.pce.networkanalyzer.port; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev230925.PathComputationRequestInput; +import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev230925.path.computation.request.input.ServiceAEnd; +import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev230925.path.computation.request.input.ServiceZEnd; +import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.port.PortBuilder; +import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.service.endpoint.sp.RxDirection; +import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.service.endpoint.sp.TxDirection; + +class PreferenceFactoryTest { + + @Test + void emptyPathComputationRequest_returnEmptyHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Map> expected = new HashMap<>(); + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceAEndRxDirectionWithoutDeviceAndPort_returnEmptyHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceAEnd serviceAEnd = Mockito.mock(ServiceAEnd.class); + RxDirection rxDirection = Mockito.mock(RxDirection.class); + + Mockito.when(rxDirection.getPort()).thenReturn(new PortBuilder().build()); + Mockito.when(serviceAEnd.getRxDirection()).thenReturn(rxDirection); + Mockito.when(pathComputationRequestInput.getServiceAEnd()).thenReturn(serviceAEnd); + + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Map> expected = new HashMap<>(); + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceAEndRxDirectionWithoutPort_returnEmptyHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceAEnd serviceAEnd = Mockito.mock(ServiceAEnd.class); + RxDirection rxDirection = Mockito.mock(RxDirection.class); + + Mockito.when(rxDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .build() + ); + Mockito.when(serviceAEnd.getRxDirection()).thenReturn(rxDirection); + Mockito.when(pathComputationRequestInput.getServiceAEnd()).thenReturn(serviceAEnd); + + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Map> expected = new HashMap<>(); + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceAEndRxDirectionTxRx_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceAEnd serviceAEnd = Mockito.mock(ServiceAEnd.class); + RxDirection rxDirection = Mockito.mock(RxDirection.class); + + Mockito.when(rxDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName("SRG1-PP1-TXRX") + .build() + ); + Mockito.when(serviceAEnd.getRxDirection()).thenReturn(rxDirection); + Mockito.when(pathComputationRequestInput.getServiceAEnd()).thenReturn(serviceAEnd); + + Map> expected = new HashMap<>(); + expected.put("ROADM-B-SRG1", Set.of("SRG1-PP1-TXRX")); + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceAEndRxDirectionTx_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceAEnd serviceAEnd = Mockito.mock(ServiceAEnd.class); + RxDirection rxDirection = Mockito.mock(RxDirection.class); + + Mockito.when(rxDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName("SRG1-PP1-TX") + .build() + ); + Mockito.when(serviceAEnd.getRxDirection()).thenReturn(rxDirection); + Mockito.when(pathComputationRequestInput.getServiceAEnd()).thenReturn(serviceAEnd); + + Map> expected = new HashMap<>(); + expected.put("ROADM-B-SRG1", Set.of("SRG1-PP1-TX")); + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceAEndRxDirectionRx_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceAEnd serviceAEnd = Mockito.mock(ServiceAEnd.class); + TxDirection txDirection = Mockito.mock(TxDirection.class); + + Mockito.when(txDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName("SRG1-PP1-RX") + .build() + ); + Mockito.when(serviceAEnd.getTxDirection()).thenReturn(txDirection); + Mockito.when(pathComputationRequestInput.getServiceAEnd()).thenReturn(serviceAEnd); + + Map> expected = new HashMap<>(); + expected.put("ROADM-B-SRG1", Set.of("SRG1-PP1-RX")); + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceZEndRx_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceZEnd serviceZEnd = Mockito.mock(ServiceZEnd.class); + RxDirection rxDirection = Mockito.mock(RxDirection.class); + + Mockito.when(rxDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName("SRG1-PP1-TXRX") + .build() + ); + Mockito.when(serviceZEnd.getRxDirection()).thenReturn(rxDirection); + Mockito.when(pathComputationRequestInput.getServiceZEnd()).thenReturn(serviceZEnd); + + Map> expected = new HashMap<>(); + expected.put("ROADM-B-SRG1", Set.of("SRG1-PP1-TXRX")); + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceZEndTxDirectionTxRx_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceZEnd serviceZEnd = Mockito.mock(ServiceZEnd.class); + TxDirection txDirection = Mockito.mock(TxDirection.class); + + Mockito.when(txDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName("SRG1-PP1-TXRX") + .build() + ); + Mockito.when(serviceZEnd.getTxDirection()).thenReturn(txDirection); + Mockito.when(pathComputationRequestInput.getServiceZEnd()).thenReturn(serviceZEnd); + + Map> expected = new HashMap<>(); + expected.put("ROADM-B-SRG1", Set.of("SRG1-PP1-TXRX")); + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceZEndTxDirectionTx_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceZEnd serviceZEnd = Mockito.mock(ServiceZEnd.class); + TxDirection txDirection = Mockito.mock(TxDirection.class); + + Mockito.when(txDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName("SRG1-PP1-TX") + .build() + ); + Mockito.when(serviceZEnd.getTxDirection()).thenReturn(txDirection); + Mockito.when(pathComputationRequestInput.getServiceZEnd()).thenReturn(serviceZEnd); + + Map> expected = new HashMap<>(); + expected.put("ROADM-B-SRG1", Set.of("SRG1-PP1-TX")); + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathComputationRequestServiceZEndTxDirectionRx_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceZEnd serviceZEnd = Mockito.mock(ServiceZEnd.class); + TxDirection txDirection = Mockito.mock(TxDirection.class); + + Mockito.when(txDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName("SRG1-PP1-RX") + .build() + ); + Mockito.when(serviceZEnd.getTxDirection()).thenReturn(txDirection); + Mockito.when(pathComputationRequestInput.getServiceZEnd()).thenReturn(serviceZEnd); + + Map> expected = new HashMap<>(); + expected.put("ROADM-B-SRG1", Set.of("SRG1-PP1-RX")); + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathEmptyComputationRequestServiceZEndTx_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceZEnd serviceZEnd = Mockito.mock(ServiceZEnd.class); + TxDirection txDirection = Mockito.mock(TxDirection.class); + + Mockito.when(txDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName(" ") + .build() + ); + Mockito.when(serviceZEnd.getTxDirection()).thenReturn(txDirection); + Mockito.when(pathComputationRequestInput.getServiceZEnd()).thenReturn(serviceZEnd); + + Map> expected = new HashMap<>(); + + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void pathUnexpectedPortName_returnHashmap() { + + PathComputationRequestInput pathComputationRequestInput = Mockito.mock(PathComputationRequestInput.class); + ServiceZEnd serviceZEnd = Mockito.mock(ServiceZEnd.class); + TxDirection txDirection = Mockito.mock(TxDirection.class); + + Mockito.when(txDirection.getPort()).thenReturn( + new PortBuilder() + .setPortDeviceName("ROADM-B-SRG1") + .setPortName("FUBAR") + .build() + ); + Mockito.when(serviceZEnd.getTxDirection()).thenReturn(txDirection); + Mockito.when(pathComputationRequestInput.getServiceZEnd()).thenReturn(serviceZEnd); + + Map> expected = new HashMap<>(); + expected.put("ROADM-B-SRG1", Set.of("FUBAR")); + + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + Assertions.assertEquals(expected, portPreferenceFactory.nodePortMap(pathComputationRequestInput)); + + } + + @Test + void addingMultiplePort() { + + PreferenceFactory portPreferenceFactory = new PreferenceFactory(); + Map> mapper = new HashMap<>(); + + //New ports + Assertions.assertTrue(portPreferenceFactory.add("ROADM-B-SRG1", "SRG1-PP1-TXRX", mapper)); + Assertions.assertTrue(portPreferenceFactory.add("ROADM-B-SRG1", "SRG1-PP2-TXRX", mapper)); + Assertions.assertTrue(portPreferenceFactory.add("ROADM-B-SRG1", "SRG1-PP3-RX", mapper)); + Assertions.assertTrue(portPreferenceFactory.add("ROADM-B-SRG1", "SRG1-PP3-TX", mapper)); + + //This port already exists, should return false. + Assertions.assertFalse(portPreferenceFactory.add("ROADM-B-SRG1", "SRG1-PP2-TXRX", mapper)); + + Assertions.assertEquals( + Set.of("SRG1-PP1-TXRX", "SRG1-PP2-TXRX", "SRG1-PP3-RX", "SRG1-PP3-TX"), + mapper.get("ROADM-B-SRG1") + ); + } +} \ No newline at end of file