--- /dev/null
+/*
+ * 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<String, Set<String>> 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<String, Set<String>> nodePortMap(PathComputationRequestInput pathComputationRequestInput) {
+
+ Map<String, Set<String>> 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<String, Set<String>> 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;
+ }
+}
--- /dev/null
+/*
+ * 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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<String, Set<String>> 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