TAPI Connectivity service RPCs impl
[transportpce.git] / tapi / src / main / java / org / opendaylight / transportpce / tapi / listeners / TapiRendererListenerImpl.java
diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/listeners/TapiRendererListenerImpl.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/listeners/TapiRendererListenerImpl.java
new file mode 100644 (file)
index 0000000..d177cae
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright © 2021 Nokia, Inc. 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.tapi.listeners;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.nio.charset.Charset;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.transportpce.common.network.NetworkTransactionImpl;
+import org.opendaylight.transportpce.common.network.NetworkTransactionService;
+import org.opendaylight.transportpce.common.network.RequestProcessor;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev201125.RendererRpcResultSp;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev201125.TransportpceRendererListener;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.AdministrativeState;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.Context;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.LifecycleState;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.OperationalState;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.Uuid;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.Context1;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionBuilder;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionKey;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityService;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityServiceBuilder;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityServiceKey;
+import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.service.Connection;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TapiRendererListenerImpl implements TransportpceRendererListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TapiRendererListenerImpl.class);
+    private final DataBroker dataBroker;
+    private Uuid serviceUuid;
+    private RendererRpcResultSp serviceRpcResultSp;
+    private final NetworkTransactionService networkTransactionService;
+
+    public TapiRendererListenerImpl(DataBroker dataBroker) {
+        this.dataBroker = dataBroker;
+        this.networkTransactionService = new NetworkTransactionImpl(new RequestProcessor(this.dataBroker));
+    }
+
+    @Override
+    public void onRendererRpcResultSp(RendererRpcResultSp notification) {
+        if (compareServiceRpcResultSp(notification)) {
+            LOG.warn("ServiceRpcResultSp already wired !");
+            return;
+        }
+        serviceRpcResultSp = notification;
+        int notifType = serviceRpcResultSp.getNotificationType().getIntValue();
+        LOG.info("Renderer '{}' Notification received : {}", serviceRpcResultSp.getNotificationType().getName(),
+                notification);
+        /* service-implementation-request. */
+        if (notifType == 3) {
+            onServiceImplementationResult(notification);
+        }
+    }
+
+    /**
+     * Process service implementation result for serviceName.
+     * @param notification RendererRpcResultSp
+     */
+    private void onServiceImplementationResult(RendererRpcResultSp notification) {
+        switch (serviceRpcResultSp.getStatus()) {
+            case Successful:
+                onSuccededServiceImplementation();
+                break;
+            case Failed:
+                onFailedServiceImplementation(notification.getServiceName());
+                break;
+            case  Pending:
+                LOG.warn("Service Implementation still pending according to RpcStatusEx");
+                break;
+            default:
+                LOG.warn("Service Implementation has an unknown RpcStatusEx code");
+                break;
+        }
+    }
+
+    /**
+     * Process succeeded service implementation for service.
+     */
+    private void onSuccededServiceImplementation() {
+        LOG.info("Service implemented !");
+        // TODO: update Connections and Connectivity Service states
+        ConnectivityService connectivityService = getConnectivityService(this.serviceUuid);
+        if (connectivityService == null) {
+            LOG.error("Couldnt retrieve service from datastore");
+            return;
+        }
+        LOG.info("Connectivity service = {}", connectivityService.toString());
+        // TODO --> this throws error because the renderer goes really fast. Is this normal??
+        ConnectivityService updtConnServ = new ConnectivityServiceBuilder(connectivityService)
+            .setAdministrativeState(AdministrativeState.UNLOCKED)
+            .setLifecycleState(LifecycleState.INSTALLED)
+            .setOperationalState(OperationalState.ENABLED)
+            .build();
+        for (Connection connection:updtConnServ.getConnection().values()) {
+            updateConnectionState(connection.getConnectionUuid());
+        }
+        updateConnectivityService(updtConnServ);
+    }
+
+    /**
+     * Process failed service implementation for serviceName.
+     * @param serviceName String
+     */
+    private void onFailedServiceImplementation(String serviceName) {
+        LOG.error("Renderer implementation failed !");
+        LOG.info("PCE cancel resource done OK !");
+        Uuid suuid = new Uuid(UUID.nameUUIDFromBytes(serviceName.getBytes(Charset.forName("UTF-8")))
+                .toString());
+        // get connections of connectivity service and remove them from tapi context and then remove
+        //  service from context. The CEPs are maintained as they could be reused by another service
+        ConnectivityService connService = getConnectivityService(suuid);
+        if (connService == null) {
+            LOG.error("Service doesnt exist in tapi context");
+            return;
+        }
+        for (Connection connection:connService.getConnection().values()) {
+            deleteConnection(connection.getConnectionUuid());
+        }
+        deleteConnectivityService(suuid);
+    }
+
+    @SuppressFBWarnings(
+            value = "ES_COMPARING_STRINGS_WITH_EQ",
+            justification = "false positives, not strings but real object references comparisons")
+    private Boolean compareServiceRpcResultSp(RendererRpcResultSp notification) {
+        if (serviceRpcResultSp == null) {
+            return false;
+        }
+        if (serviceRpcResultSp.getNotificationType() != notification.getNotificationType()) {
+            return false;
+        }
+        if (serviceRpcResultSp.getServiceName() != notification.getServiceName()) {
+            return false;
+        }
+        if (serviceRpcResultSp.getStatus() != notification.getStatus()) {
+            return false;
+        }
+        if (serviceRpcResultSp.getStatusMessage() != notification.getStatusMessage()) {
+            return false;
+        }
+        return true;
+    }
+
+    private ConnectivityService getConnectivityService(Uuid suuid) {
+        // TODO: verify this is correct. Should we identify the context IID with the context UUID??
+        try {
+            // First read connectivity service with service uuid and update info
+            InstanceIdentifier<ConnectivityService> connectivityServIID =
+                InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
+                    .child(org.opendaylight.yang.gen.v1.urn
+                        .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
+                    .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
+                    .build();
+
+            Optional<ConnectivityService> optConnServ =
+                this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectivityServIID).get();
+            if (!optConnServ.isPresent()) {
+                LOG.error("Connectivity service not found in tapi context");
+                return null;
+            }
+            return optConnServ.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Failed to merge TAPI connectivity", e);
+            return null;
+        }
+    }
+
+    private void updateConnectionState(Uuid connectionUuid) {
+        // TODO: verify this is correct. Should we identify the context IID with the context UUID??
+        try {
+            // First read connection with connection uuid and update info
+            InstanceIdentifier<org.opendaylight.yang.gen.v1.urn
+                .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> connectionIID =
+                InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
+                    .child(org.opendaylight.yang.gen.v1.urn
+                        .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
+                    .child(org.opendaylight.yang.gen.v1.urn
+                            .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection.class,
+                        new ConnectionKey(connectionUuid))
+                    .build();
+
+            Optional<org.opendaylight.yang.gen.v1.urn
+                .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> optConn =
+                this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectionIID).get();
+            if (!optConn.isPresent()) {
+                LOG.error("Connection not found in tapi context");
+                return;
+            }
+            org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection
+                newConnection = new ConnectionBuilder(optConn.get()).setLifecycleState(LifecycleState.INSTALLED)
+                    .setOperationalState(OperationalState.ENABLED).build();
+            // merge in datastore
+            this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connectionIID,
+                    newConnection);
+            this.networkTransactionService.commit().get();
+            LOG.info("TAPI connection merged successfully.");
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Failed to merge TAPI connection", e);
+        }
+    }
+
+    private void updateConnectivityService(ConnectivityService updtConnServ) {
+        // TODO: verify this is correct. Should we identify the context IID with the context UUID??
+        try {
+            // First read connectivity service with connectivity service uuid and update info
+            InstanceIdentifier<ConnectivityService> connServIID =
+                InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
+                    .child(org.opendaylight.yang.gen.v1.urn
+                        .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
+                    .child(ConnectivityService.class, new ConnectivityServiceKey(updtConnServ.getUuid()))
+                    .build();
+
+            Optional<ConnectivityService> optConnServ =
+                this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connServIID).get();
+            if (!optConnServ.isPresent()) {
+                LOG.error("Connection not found in tapi context");
+                return;
+            }
+            ConnectivityService newConnServ = new ConnectivityServiceBuilder(updtConnServ).build();
+            // merge in datastore
+            this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connServIID,
+                    newConnServ);
+            this.networkTransactionService.commit().get();
+            LOG.info("TAPI connectivity service merged successfully.");
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Failed to merge TAPI connectivity service", e);
+        }
+    }
+
+    private void deleteConnectivityService(Uuid suuid) {
+        // First read connectivity service with service uuid and update info
+        InstanceIdentifier<ConnectivityService> connectivityServIID =
+            InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
+                .child(org.opendaylight.yang.gen.v1.urn
+                    .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
+                .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
+                .build();
+        try {
+            this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectivityServIID);
+            this.networkTransactionService.commit().get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Failed to delete TAPI connectivity service", e);
+        }
+    }
+
+    private void deleteConnection(Uuid connectionUuid) {
+        // First read connectivity service with service uuid and update info
+        InstanceIdentifier<org.opendaylight.yang.gen.v1
+            .urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> connectionIID =
+            InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
+                .child(org.opendaylight.yang.gen.v1.urn
+                    .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
+                .child(org.opendaylight.yang.gen.v1.urn
+                        .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection.class,
+                    new org.opendaylight.yang.gen.v1.urn
+                        .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionKey(
+                            connectionUuid))
+                .build();
+        try {
+            this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectionIID);
+            this.networkTransactionService.commit().get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Failed to delete TAPI connection", e);
+        }
+    }
+
+    public void setServiceUuid(Uuid serviceUuid) {
+        this.serviceUuid = serviceUuid;
+    }
+}