/*
* Copyright © 2021 Nokia. 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.topology;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
import org.opendaylight.mdsal.binding.api.DataTreeModification;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.transportpce.common.NetworkUtils;
import org.opendaylight.transportpce.common.network.NetworkTransactionService;
import org.opendaylight.transportpce.tapi.TapiStringConstants;
import org.opendaylight.transportpce.tapi.utils.TapiLink;
import org.opendaylight.yang.gen.v1.http.org.openroadm.common.network.rev211210.Link1;
import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev211210.OpenroadmLinkType;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NetworkId;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.Networks;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.networks.Network;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.networks.NetworkKey;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.LinkId;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.Network1;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.networks.network.Link;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.networks.network.LinkKey;
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.LayerProtocolName;
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.topology.rev181210.Context1;
import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev181210.context.TopologyContext;
import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev181210.topology.context.Topology;
import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev181210.topology.context.TopologyBuilder;
import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev181210.topology.context.TopologyKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TapiOrLinkListener implements DataTreeChangeListener {
private static final Logger LOG = LoggerFactory.getLogger(TapiOrLinkListener.class);
private final TapiLink tapiLink;
private final NetworkTransactionService networkTransactionService;
private final Uuid tapiTopoUuid = new Uuid(UUID.nameUUIDFromBytes(TapiStringConstants.T0_FULL_MULTILAYER
.getBytes(Charset.forName("UTF-8"))).toString());
public TapiOrLinkListener(final TapiLink tapiLink, final NetworkTransactionService networkTransactionService) {
this.tapiLink = tapiLink;
this.networkTransactionService = networkTransactionService;
}
@Override
public void onDataTreeChanged(@NonNull Collection> changes) {
LOG.info("onDataTreeChanged - {}", this.getClass().getSimpleName());
for (DataTreeModification change : changes) {
if (change.getRootNode().getDataBefore() == null && change.getRootNode().getDataAfter() != null) {
LOG.info("New link in openroadm topology");
Link link = change.getRootNode().getDataAfter();
// Todo: XPDR links are unidirectional, therefore we need to check for the current one and
// the opposite one. But first check the type
Link1 link1 = link.augmentation(Link1.class);
if (link1 == null) {
LOG.error("No type in link. We cannot trigger the TAPI link creation");
return;
}
if (!(link1.getLinkType().equals(OpenroadmLinkType.XPONDERINPUT)
|| link1.getLinkType().equals(OpenroadmLinkType.XPONDEROUTPUT))) {
LOG.warn("Not triggering creation of link for type = {}", link1.getLinkType().getName());
return;
}
if (!oppositeLinkExists(link1.getOppositeLink())) {
LOG.warn("Opposite link doest exist. Not creating TAPI link");
return;
}
LOG.info("Opposite link already in datastore. Creatin TAPI bidirectional link");
String srcNode = getRoadmOrXpdr(link.getSource().getSourceNode().getValue());
String srcTp = link.getSource().getSourceTp().getValue();
String destNode = getRoadmOrXpdr(link.getDestination().getDestNode().getValue());
String destTp = link.getDestination().getDestTp().getValue();
putTapiLinkInTopology(this.tapiLink.createTapiLink(srcNode, srcTp, destNode, destTp,
TapiStringConstants.OMS_XPDR_RDM_LINK, getQual(srcNode), getQual(destNode),
TapiStringConstants.PHTNC_MEDIA, TapiStringConstants.PHTNC_MEDIA,
link1.getAdministrativeState().getName(), link1.getOperationalState().getName(),
Set.of(LayerProtocolName.PHOTONICMEDIA), Set.of(LayerProtocolName.PHOTONICMEDIA.getName()),
tapiTopoUuid));
}
}
}
private void putTapiLinkInTopology(
org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev181210.topology.Link tapiXpdrLink) {
LOG.info("Creating tapi link in TAPI topology context");
InstanceIdentifier topoIID = InstanceIdentifier.builder(Context.class)
.augmentation(Context1.class).child(TopologyContext.class)
.child(Topology.class, new TopologyKey(this.tapiTopoUuid))
.build();
Topology topology = new TopologyBuilder().setUuid(this.tapiTopoUuid)
.setLink(Map.of(tapiXpdrLink.key(), tapiXpdrLink)).build();
// merge in datastore
this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, topoIID,
topology);
try {
this.networkTransactionService.commit().get();
} catch (InterruptedException | ExecutionException e) {
LOG.error("Error populating TAPI topology: ", e);
}
LOG.info("TAPI Link added succesfully.");
}
private String getQual(String node) {
if (node.contains("ROADM")) {
return TapiStringConstants.PHTNC_MEDIA;
}
return TapiStringConstants.OTSI;
}
private boolean oppositeLinkExists(LinkId oppositeLink) {
try {
InstanceIdentifier linkIID = InstanceIdentifier.builder(Networks.class)
.child(Network.class, new NetworkKey(new NetworkId(NetworkUtils.OVERLAY_NETWORK_ID)))
.augmentation(Network1.class).child(Link.class, new LinkKey(oppositeLink)).build();
Optional optLink =
this.networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, linkIID).get();
if (!optLink.isPresent()) {
LOG.error("Opposite link not found in datastore {}", oppositeLink.getValue());
return false;
}
return true;
} catch (InterruptedException | ExecutionException e) {
LOG.error("Failed to read opposite link", e);
return false;
}
}
private String getRoadmOrXpdr(String node) {
if (node.contains("ROADM")) {
return String.join("-", node.split("-")[0], node.split("-")[1]);
}
return node;
}
}