ONOS-5304 MDSC TE Tunnel controller
Change-Id: I1edbf3a583966f1389eb167543ced7bd6c4c51b3
ONOS-5304 MDSC TE Tunnel controller
Change-Id: I1edbf3a583966f1389eb167543ced7bd6c4c51b3
diff --git a/apps/actn-mdsc/tetunnel-ctl/pom.xml b/apps/actn-mdsc/tetunnel-ctl/pom.xml
new file mode 100644
index 0000000..c2f678f
--- /dev/null
+++ b/apps/actn-mdsc/tetunnel-ctl/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>onos-actn-mdsc</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.8.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-actn-mdsc-tetunnel-ctl</artifactId>
+ <description>TE Tunnel controller which manages TE tunnel processing</description>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-tetunnel-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-actn-mdsc-tetunnel-pce</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/apps/actn-mdsc/tetunnel-ctl/src/main/java/org/onosproject/actn/mdsc/tetunnelctl/TeTunnelCtl.java b/apps/actn-mdsc/tetunnel-ctl/src/main/java/org/onosproject/actn/mdsc/tetunnelctl/TeTunnelCtl.java
new file mode 100755
index 0000000..5106652
--- /dev/null
+++ b/apps/actn-mdsc/tetunnel-ctl/src/main/java/org/onosproject/actn/mdsc/tetunnelctl/TeTunnelCtl.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.actn.mdsc.tetunnelctl;
+
+import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.actn.mdsc.pce.TeTunnelPceService;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelAdminService;
+import org.onosproject.incubator.net.tunnel.TunnelEvent;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelListener;
+import org.onosproject.incubator.net.tunnel.TunnelService;
+import org.onosproject.tetopology.management.api.TeTopology;
+import org.onosproject.tetopology.management.api.TeTopologyKey;
+import org.onosproject.tetopology.management.api.TeTopologyService;
+import org.onosproject.tetopology.management.api.node.TeNodeKey;
+import org.onosproject.tetopology.management.api.node.TtpKey;
+import org.onosproject.tetunnel.api.TeTunnelAdminService;
+import org.onosproject.tetunnel.api.TeTunnelService;
+import org.onosproject.tetunnel.api.tunnel.DefaultTeTunnel;
+import org.onosproject.tetunnel.api.tunnel.TeTunnel;
+import org.onosproject.tetunnel.api.tunnel.TeTunnelKey;
+import org.onosproject.tetunnel.api.tunnel.path.TePath;
+import org.onosproject.tetunnel.api.tunnel.path.TeRouteSubobject;
+import org.onosproject.tetunnel.api.tunnel.path.TeRouteUnnumberedLink;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * TE Tunnel controller/processor which manages TE tunnel processing.
+ * <p>
+ * For example, when creating a cross-domain tunnel from a MDSC, the
+ * processor will call a relevant PCE to get an end-to-end cross-domain path,
+ * then spits the path into segment tunnels(domain tunnels), and then informs
+ * PNCs to setup domain tunnels respectively.
+ */
+@Component(immediate = true)
+public class TeTunnelCtl {
+
+ private static final Logger log = LoggerFactory.getLogger(TeTunnelCtl.class);
+
+ private final TunnelListener tunnelListener = new InternalTunnelListener();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TunnelService tunnelService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TunnelAdminService tunnelAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TeTunnelService teTunnelService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TeTunnelAdminService teTunnelAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TeTopologyService teTopologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TeTunnelPceService teTunnelPceService;
+
+ @Activate
+ protected void activate() {
+ tunnelService.addListener(tunnelListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ tunnelService.removeListener(tunnelListener);
+
+ log.info("Stopped");
+ }
+
+ private void addTeTunnel(TeTunnel teTunnel) {
+ if (teTunnel == null) {
+ return;
+ }
+
+ Tunnel tunnel = tunnelService.queryTunnel(
+ teTunnelService.getTunnelId(teTunnel.teTunnelKey()));
+ if (tunnel == null) {
+ log.error("tunnel does not exist, {}", teTunnel.teTunnelKey());
+ return;
+ }
+ if (tunnel.state() != Tunnel.State.INIT) {
+ log.error("tunnel state error, {}, {}", teTunnel.teTunnelKey(),
+ tunnel.state());
+ return;
+ }
+ tunnelAdminService.updateTunnelState(tunnel, Tunnel.State.ESTABLISHING);
+
+ //TODO support multi-thread
+ if (isTeTunnelCrossDomain(teTunnel)) {
+ if (!addCrossDomainTeTunnel(teTunnel)) {
+ tunnelAdminService.updateTunnelState(tunnel, Tunnel.State.FAILED);
+ }
+ }
+ /*
+ * "else" is to do nothing.
+ * When adding a single domain tunnel, the TunnelManager will call
+ * tunnel providers, then the providers will pass the request to
+ * the domain controller. Nothing to do here.
+ */
+ }
+
+ private boolean isTeTunnelCrossDomain(TeTunnel teTunnel) {
+ TeTopology srcTopo = teTopologyService.teTopology(
+ teTopologyService.teNode(teTunnel.srcNode())
+ .underlayTeTopologyId());
+ TeTopology dstTopo = teTopologyService.teTopology(
+ teTopologyService.teNode(teTunnel.dstNode())
+ .underlayTeTopologyId());
+ return (srcTopo != null && dstTopo != null
+ && srcTopo.ownerId().equals(dstTopo.ownerId()));
+ }
+
+ private boolean addCrossDomainTeTunnel(TeTunnel teTunnel) {
+ List<TeRouteSubobject> route = null;
+ TePath primaryPath = teTunnel.primaryPaths().get(0);
+ if (primaryPath != null &&
+ primaryPath.type() == TePath.Type.EXPLICIT) {
+ route = primaryPath.explicitRoute();
+ } else {
+ Collection<List<TeRouteSubobject>> routes =
+ teTunnelPceService.computePaths(teTunnel);
+ if (routes == null || routes.isEmpty()) {
+ log.error("no available route for {}",
+ teTunnel.teTunnelKey());
+ return false;
+ }
+
+ //FIXME: try other pce when failed?
+ route = routes.iterator().next();
+ }
+
+ if (route == null) {
+ log.error("no available route for {}",
+ teTunnel.teTunnelKey());
+ return false;
+ }
+
+ return spitRoute(teTunnel, route);
+ }
+
+ //spits route to segment tunnels
+ private boolean spitRoute(TeTunnel teTunnel, List<TeRouteSubobject> route) {
+ List<TeTunnelKey> segmentTunnels = Lists.newArrayList();
+ boolean success = true;
+ TeNodeKey srcNode = teTunnel.srcNode();
+ TtpKey srcTp = teTunnel.srcTp();
+ TeNodeKey dstNode = null;
+ TtpKey dstTp = null;
+
+ for (TeRouteSubobject teRouteSubobject : route) {
+ if (!(teRouteSubobject instanceof TeRouteUnnumberedLink)) {
+ log.error("unsupported type {}", teRouteSubobject.type());
+ success = false;
+ break;
+ }
+
+ TeRouteUnnumberedLink teRouteUnnumberedLink =
+ (TeRouteUnnumberedLink) teRouteSubobject;
+ dstNode = teRouteUnnumberedLink.node();
+ dstTp = teRouteUnnumberedLink.ttp();
+ if (Objects.equals(srcNode, dstNode) &&
+ Objects.equals(srcTp, dstTp)) {
+ continue;
+ }
+ if (Objects.equals(srcNode, dstNode)) {
+ if (!addSegmentTunnel(segmentTunnels, teTunnel,
+ srcNode, srcTp, dstNode, dstTp)) {
+ success = false;
+ break;
+ }
+ }
+
+ srcNode = dstNode;
+ srcTp = dstTp;
+ }
+
+ if (success && !(Objects.equals(dstNode, teTunnel.dstNode()) &&
+ Objects.equals(dstTp, teTunnel.dstTp()))) {
+ srcNode = dstNode;
+ srcTp = dstTp;
+ dstNode = teTunnel.dstNode();
+ dstTp = teTunnel.dstTp();
+ if (!addSegmentTunnel(segmentTunnels, teTunnel,
+ srcNode, srcTp, dstNode, dstTp)) {
+ success = false;
+ }
+ }
+
+ if (!success) {
+ // roll back segment tunnels
+ for (TeTunnelKey key : segmentTunnels) {
+ teTunnelAdminService.removeTeTunnel(key);
+ }
+ } else {
+ teTunnelAdminService.setSegmentTunnel(teTunnel.teTunnelKey(),
+ segmentTunnels);
+ }
+ return success;
+ }
+
+ private boolean addSegmentTunnel(List<TeTunnelKey> segmentTunnels,
+ TeTunnel teTunnel,
+ TeNodeKey srcNode, TtpKey srcTp,
+ TeNodeKey dstNode, TtpKey dstTp) {
+ TeTunnelKey teTunnelKey = getNextTeTunnelKey(srcNode.teTopologyKey());
+ TeTunnel teTunnelSegment = DefaultTeTunnel.builder()
+ .teTunnelKey(teTunnelKey)
+ .srcNode(srcNode)
+ .dstNode(dstNode)
+ .srcTp(srcTp)
+ .dstTp(dstTp)
+ .adminState(teTunnel.adminStatus())
+ .lspProtectionType(teTunnel.lspProtectionType())
+ .type(teTunnel.type())
+ .build();
+ TunnelId tunnelId =
+ teTunnelAdminService.createTeTunnel(teTunnelSegment);
+ if (tunnelId == null) {
+ log.error("failed to create segment tunnel: {},{},{},{}",
+ srcNode, srcTp, dstNode, dstTp);
+ return false;
+ }
+ segmentTunnels.add(teTunnelKey);
+ return true;
+ }
+
+ private TeTunnelKey getNextTeTunnelKey(TeTopologyKey key) {
+ //FIXME need a better way to get a te tunnel id
+ long teTunnelId = teTunnelService.getTeTunnels(key).size() + 1;
+ return new TeTunnelKey(key, teTunnelId);
+ }
+
+ private void updateTeTunnel(TeTunnel teTunnel, Tunnel tunnel) {
+ if (teTunnel == null) {
+ return;
+ }
+
+ if (tunnel.state() == Tunnel.State.ESTABLISHED) {
+ tunnelEstablished(teTunnel);
+ } else if (tunnel.state() == Tunnel.State.REMOVING) {
+ removingTunnel(teTunnel);
+ }
+
+ //TODO update TE tunnel content
+ }
+
+ private void tunnelEstablished(TeTunnel teTunnel) {
+ TeTunnel e2eTeTunnel = retriveE2eTunnel(teTunnel);
+ if (e2eTeTunnel != null) {
+ boolean goodToContinue = true;
+ for (TeTunnelKey key : e2eTeTunnel.segmentTunnels()) {
+ goodToContinue = checkSegmentTunnel(key);
+ if (!goodToContinue) {
+ break;
+ }
+ }
+
+ if (goodToContinue) {
+ tunnelAdminService.updateTunnelState(
+ tunnelService.queryTunnel(
+ teTunnelService.getTunnelId(
+ teTunnel.teTunnelKey())),
+ Tunnel.State.ESTABLISHED
+ );
+ }
+ }
+ }
+
+ private TeTunnel retriveE2eTunnel(TeTunnel segmentTunnel) {
+ return teTunnelService.getTeTunnel(segmentTunnel.e2eTunnelKey());
+ }
+
+ private boolean checkSegmentTunnel(TeTunnelKey key) {
+ Tunnel segmentTunnel = tunnelService.queryTunnel(
+ teTunnelService.getTunnelId(key));
+ if (segmentTunnel == null ||
+ segmentTunnel.state() != Tunnel.State.ESTABLISHED) {
+ return false;
+ }
+ return true;
+ }
+
+ private void removingTunnel(TeTunnel teTunnel) {
+ List<TeTunnelKey> segmentTunnels = teTunnel.segmentTunnels();
+ if (segmentTunnels != null && !segmentTunnels.isEmpty()) {
+ for (TeTunnelKey key : segmentTunnels) {
+ teTunnelAdminService.removeTeTunnel(key);
+ }
+ }
+ }
+
+ // Listens on tunnel events.
+ private class InternalTunnelListener implements TunnelListener {
+ @Override
+ public void event(TunnelEvent event) {
+ switch (event.type()) {
+ case TUNNEL_ADDED:
+ addTunnel(event.subject());
+ break;
+ case TUNNEL_UPDATED:
+ updateTunnel(event.subject());
+ break;
+ //TODO: TE Tunnel remove/... event process
+ default:
+ log.warn("unknown event: {}", event.type());
+ break;
+ }
+ }
+
+ private void addTunnel(Tunnel tunnel) {
+ addTeTunnel(teTunnelService.getTeTunnel(tunnel.tunnelId()));
+ }
+
+ private void updateTunnel(Tunnel tunnel) {
+ updateTeTunnel(teTunnelService.getTeTunnel(tunnel.tunnelId()),
+ tunnel);
+ }
+ }
+}
diff --git a/apps/actn-mdsc/tetunnel-ctl/src/main/java/org/onosproject/actn/mdsc/tetunnelctl/package-info.java b/apps/actn-mdsc/tetunnel-ctl/src/main/java/org/onosproject/actn/mdsc/tetunnelctl/package-info.java
new file mode 100644
index 0000000..8de1d14
--- /dev/null
+++ b/apps/actn-mdsc/tetunnel-ctl/src/main/java/org/onosproject/actn/mdsc/tetunnelctl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * TE Tunnel controller/processor which manages TE tunnel processing.
+ */
+package org.onosproject.actn.mdsc.tetunnelctl;
\ No newline at end of file