Added E-CORD metro path provisioner.
- add copyright
- use Commons Pair
- logs to help debugging
Change-Id: Ie7e59da18396d5edc68f2740e998ea1b9750c493
diff --git a/ecord/metro/pom.xml b/ecord/metro/pom.xml
new file mode 100644
index 0000000..c3f2dfb
--- /dev/null
+++ b/ecord/metro/pom.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016 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-app-samples</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.6.0-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-ecord-metro</artifactId>
+ <version>1.6.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <description>Enterprise CORD for metro network</description>
+ <url>http://onosproject.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <onos.version>1.6.0-SNAPSHOT</onos.version>
+ <onos.app.name>org.onosproject.ecord.metro</onos.app.name>
+ <!-- TODO App dependency not working? -->
+ <onos.app.requires>org.onosproject.incubator.rpc,org.onosproject.incubator.rpc.grpc</onos.app.requires>
+ <onos.app.origin>Open Networking Lab</onos.app.origin>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <version>${onos.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-core-dist</artifactId>
+ <version>${onos.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <version>${onos.version}</version>
+ <scope>test</scope>
+ <classifier>tests</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <version>1.9.12</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>4.3.1</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${onos.version}</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <version>3.0.5</version>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.5.3</version>
+ <extensions>true</extensions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ <version>1.20.0</version>
+ <executions>
+ <execution>
+ <id>generate-scr-srcdescriptor</id>
+ <goals>
+ <goal>scr</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <supportedProjectTypes>
+ <supportedProjectType>bundle</supportedProjectType>
+ <supportedProjectType>war</supportedProjectType>
+ </supportedProjectTypes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-maven-plugin</artifactId>
+ <version>1.5</version>
+ <executions>
+ <execution>
+ <id>cfg</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>cfg</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>swagger</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>swagger</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>app</id>
+ <phase>package</phase>
+ <goals>
+ <goal>app</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/CeVlanConfig.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/CeVlanConfig.java
new file mode 100644
index 0000000..c2f4576
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/CeVlanConfig.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ecord.metro;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.Config;
+
+import java.util.Optional;
+
+/**
+ * Configuration information for edge connect point and corresponding VLAN tag.
+ */
+public class CeVlanConfig extends Config<ConnectPoint> {
+ public static final String CONFIG_KEY = "ceVlan";
+
+ public static final String CE_VLAN_TAG_KEY = "tag";
+
+ public Optional<VlanId> ceVlanId() {
+ String s = get(CE_VLAN_TAG_KEY, null);
+ if (s == null) {
+ return Optional.empty();
+ }
+ return Optional.of(VlanId.vlanId(Short.valueOf(s)));
+ }
+
+ public CeVlanConfig ceVlanId(VlanId vlanId) {
+ if (vlanId == null) {
+ return (CeVlanConfig) setOrClear(CE_VLAN_TAG_KEY, (String) null);
+ }
+ return (CeVlanConfig) setOrClear(CE_VLAN_TAG_KEY, String.valueOf(vlanId.toShort()));
+ }
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/CoPathProvisioner.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/CoPathProvisioner.java
new file mode 100644
index 0000000..6e7b168
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/CoPathProvisioner.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ecord.metro;
+
+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.onlab.packet.VlanId;
+import org.onlab.util.Bandwidth;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Main component to configure CO.
+ */
+@Component(immediate = true)
+public class CoPathProvisioner {
+ protected static final Logger log = LoggerFactory.getLogger(CoPathProvisioner.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Activate
+ protected void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ log.info("Stopped");
+ }
+
+ public boolean setupCoConnectivity(DeviceId bigSw, ConnectPoint uni, ConnectPoint egress,
+ VlanId cevlan, Bandwidth bandwidth) {
+ // TODO implement
+ return false;
+ }
+
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/MetroConnectivity.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/MetroConnectivity.java
new file mode 100644
index 0000000..7b86cc9
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/MetroConnectivity.java
@@ -0,0 +1,172 @@
+/*
+ * 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.ecord.metro;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.util.Bandwidth;
+import org.onosproject.ecord.metro.api.MetroConnectivityId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.intent.IntentId;
+
+import java.time.Duration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Entity to store metro connectivity request and related information.
+ */
+public class MetroConnectivity {
+
+ private final MetroConnectivityId id;
+ private final List<Link> links;
+ private final Bandwidth requestBandwidth;
+ private final Duration requestLatency;
+
+ // Bandwidth capacity of optical layer
+ private Bandwidth opticalCapacity;
+
+ private final Set<PacketLinkRealizedByOptical> realizingLinks = new HashSet<>();
+
+ // TODO This IntentId is used only to reserve bandwidth resource.
+ // If ResourceManager can accept app-defined ResourceConsumer (not IntentId),
+ // this Intent should be replaced with MetroConnectivityId.
+ private IntentId intentId;
+
+ private State state = State.CREATED;
+
+ public enum State {
+ CREATED,
+ INSTALLING,
+ INSTALLED,
+ WITHDRAWING,
+ WITHDRAWN,
+ FAILED
+ }
+
+ public MetroConnectivity(MetroConnectivityId id, Path path, Bandwidth requestBandwidth,
+ Duration requestLatency) {
+ this.id = id;
+ this.links = ImmutableList.copyOf(path.links());
+ this.requestBandwidth = requestBandwidth;
+ this.requestLatency = requestLatency;
+ }
+
+ public void setLinkEstablished(ConnectPoint src, ConnectPoint dst) {
+ realizingLinks.stream().filter(l -> l.equals(src, dst))
+ .findAny()
+ .ifPresent(l -> l.setEstablished(true));
+ }
+
+ public void setLinkRemoved(ConnectPoint src, ConnectPoint dst) {
+ realizingLinks.stream().filter(l -> l.equals(src, dst))
+ .findAny()
+ .ifPresent(l -> l.setEstablished(false));
+ }
+
+ public boolean isAllRealizingLinkEstablished() {
+ return realizingLinks.stream().allMatch(PacketLinkRealizedByOptical::isEstablished);
+ }
+
+ public boolean isAllRealizingLinkNotEstablished() {
+ return !realizingLinks.stream().anyMatch(PacketLinkRealizedByOptical::isEstablished);
+ }
+
+ public MetroConnectivityId id() {
+ return id;
+ }
+
+ public List<Link> links() {
+ return links;
+ }
+
+ public Bandwidth bandwidth() {
+ return requestBandwidth;
+ }
+
+ public Duration latency() {
+ return requestLatency;
+ }
+
+ public State state() {
+ return state;
+ }
+
+ public boolean state(State state) {
+ boolean valid = true;
+ // reject invalid state transition
+ switch (this.state) {
+ case CREATED:
+ valid = (state == State.INSTALLING || state == State.FAILED);
+ break;
+ case INSTALLING:
+ valid = (state == State.INSTALLED || state == State.FAILED);
+ break;
+ case INSTALLED:
+ valid = (state == State.WITHDRAWING || state == State.FAILED);
+ break;
+ case WITHDRAWING:
+ valid = (state == State.WITHDRAWN || state == State.FAILED);
+ break;
+ case FAILED:
+ valid = (state == State.INSTALLING || state == State.WITHDRAWING || state == State.FAILED);
+ break;
+ default:
+ break;
+ }
+
+ if (valid) {
+ this.state = state;
+ }
+
+ return valid;
+ }
+
+ public Bandwidth getOpticalCapacity() {
+ return opticalCapacity;
+ }
+
+ public void setOpticalCapacity(Bandwidth opticalCapacity) {
+ this.opticalCapacity = opticalCapacity;
+ }
+
+ public void addRealizingLink(PacketLinkRealizedByOptical link) {
+ checkNotNull(link);
+ realizingLinks.add(link);
+ }
+
+ public void removeRealizingLink(PacketLinkRealizedByOptical link) {
+ checkNotNull(link);
+ realizingLinks.remove(link);
+ }
+
+ public Set<PacketLinkRealizedByOptical> getRealizingLinks() {
+ return ImmutableSet.copyOf(realizingLinks);
+ }
+
+ public IntentId getIntentId() {
+ return intentId;
+ }
+
+ public void setIntentId(IntentId intentId) {
+ this.intentId = intentId;
+ }
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/MetroPathProvisioner.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/MetroPathProvisioner.java
new file mode 100644
index 0000000..7fa0092
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/MetroPathProvisioner.java
@@ -0,0 +1,726 @@
+/*
+ * 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.ecord.metro;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import org.apache.commons.lang3.tuple.Pair;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Bandwidth;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.ecord.metro.api.MetroConnectivityId;
+import org.onosproject.ecord.metro.api.MetroPathEvent;
+import org.onosproject.ecord.metro.api.MetroPathListener;
+import org.onosproject.ecord.metro.api.MetroPathService;
+import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.CltSignalType;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.OchPort;
+import org.onosproject.net.OduCltPort;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.Path;
+import org.onosproject.net.Port;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.OpticalCircuitIntent;
+import org.onosproject.net.intent.OpticalConnectivityIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.resource.BandwidthCapacity;
+import org.onosproject.net.resource.ContinuousResource;
+import org.onosproject.net.resource.Resource;
+import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceService;
+import org.onosproject.net.resource.Resources;
+import org.onosproject.net.topology.LinkWeight;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.TopologyEdge;
+import org.onosproject.store.service.AtomicCounter;
+import org.onosproject.store.service.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.config.basics.SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY;
+
+/**
+ * Main component to configure metro area connectivity.
+ */
+@Service
+@Component(immediate = true)
+public class MetroPathProvisioner
+ extends AbstractListenerManager<MetroPathEvent, MetroPathListener>
+ implements MetroPathService {
+ protected static final Logger log = LoggerFactory.getLogger(MetroPathProvisioner.class);
+
+ private static final String METRO_CONNECTIVITY_ID_COUNTER = "ecord-metro-connectivity-id";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry cfgRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ResourceService resourceService;
+
+ private final List<ConfigFactory<?, ?>> factories = ImmutableList.of(
+ new ConfigFactory<ConnectPoint, CeVlanConfig>(CONNECT_POINT_SUBJECT_FACTORY,
+ CeVlanConfig.class, CeVlanConfig.CONFIG_KEY) {
+ @Override
+ public CeVlanConfig createConfig() {
+ return new CeVlanConfig();
+ }
+ });
+
+ private ApplicationId appId;
+
+ private AtomicCounter idCounter;
+
+ private IntentListener intentListener = new InternalIntentListener();
+ private NetworkConfigListener netcfgListener = new InternalNetworkConfigListener();
+
+ private Map<PacketLinkRealizedByOptical, MetroConnectivity> linkPathMap = new ConcurrentHashMap<>();
+
+ // TODO this should be stored to distributed store
+ private Map<MetroConnectivityId, MetroConnectivity> connectivities = new ConcurrentHashMap<>();
+
+ // TODO this should be stored to distributed store
+ // Map of cross connect link and installed path which uses the link
+ private Set<Link> usedCrossConnectLinks = Sets.newConcurrentHashSet();
+
+ // Map of connect points and corresponding VLAN tag
+ private Map<ConnectPoint, VlanId> portVlanMap = new ConcurrentHashMap<>();
+
+ @Activate
+ protected void activate() {
+ factories.forEach(cfgRegistry::registerConfigFactory);
+ appId = coreService.registerApplication("org.onosproject.ecord.metro");
+
+ idCounter = storageService.atomicCounterBuilder()
+ .withName(METRO_CONNECTIVITY_ID_COUNTER)
+ .withMeteringDisabled()
+ .build()
+ .asAtomicCounter();
+
+ eventDispatcher.addSink(MetroPathEvent.class, listenerRegistry);
+ intentService.addListener(intentListener);
+ networkConfigService.addListener(netcfgListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ networkConfigService.removeListener(netcfgListener);
+ intentService.removeListener(intentListener);
+
+ factories.forEach(cfgRegistry::unregisterConfigFactory);
+
+ log.info("Stopped");
+ }
+
+ @Override
+ public MetroConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
+ Bandwidth bandwidth, Duration latency) {
+ checkNotNull(ingress);
+ checkNotNull(egress);
+ log.info("setupConnectivity({}, {}, {}, {})", ingress, egress, bandwidth, latency);
+
+ bandwidth = (bandwidth == null) ? Bandwidth.bps(0) : bandwidth;
+
+ Set<Path> paths = pathService.getPaths(ingress.deviceId(), egress.deviceId(),
+ new BandwidthLinkWeight(bandwidth));
+ if (paths.isEmpty()) {
+ log.warn("Unable to find multi-layer path.");
+ return null;
+ }
+
+ // Search path with available cross connect points
+ for (Path path : paths) {
+ MetroConnectivityId id = setupPath(path, bandwidth, latency);
+ if (id != null) {
+ log.info("Assigned MetroConnectivityId: {}", id);
+ return id;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public MetroConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency) {
+ checkNotNull(path);
+ log.info("setupPath({}, {}, {})", path, bandwidth, latency);
+
+ // validate optical path
+ List<Pair<ConnectPoint, ConnectPoint>> xcPointPairs = getCrossConnectPoints(path);
+ if (!checkXcPoints(xcPointPairs)) {
+ // Can't setup path if cross connect points are mismatched
+ return null;
+ }
+
+ MetroConnectivity connectivity = createConnectivity(path, bandwidth, latency);
+
+ // create intents from cross connect points and set connectivity information
+ List<Intent> intents = createIntents(xcPointPairs, connectivity);
+
+ // store cross connect port usage
+ path.links().stream().filter(this::isCrossConnectLink)
+ .forEach(usedCrossConnectLinks::add);
+
+ // Submit the intents
+ for (Intent i : intents) {
+ intentService.submit(i);
+ log.debug("Submitted an intent: {}", i);
+ }
+
+ return connectivity.id();
+ }
+
+ private MetroConnectivity createConnectivity(Path path, Bandwidth bandwidth, Duration latency) {
+ MetroConnectivityId id = MetroConnectivityId.valueOf(idCounter.getAndIncrement());
+ MetroConnectivity connectivity = new MetroConnectivity(id, path, bandwidth, latency);
+
+ ConnectPoint ingress = path.src();
+ ConnectPoint egress = path.dst();
+
+ Intent pktIntent = PointToPointIntent.builder()
+ .appId(appId)
+ .ingressPoint(ingress)
+ .egressPoint(egress)
+ .key(Key.of(id.value(), appId))
+ .build();
+
+ connectivity.setIntentId(pktIntent.id());
+
+ // store connectivity information
+ connectivities.put(connectivity.id(), connectivity);
+
+ return connectivity;
+ }
+
+ @Override
+ public boolean removeConnectivity(MetroConnectivityId id) {
+ log.info("removeConnectivity({})", id);
+ MetroConnectivity connectivity = connectivities.remove(id);
+
+ // TODO withdraw intent only if all of connectivities that use the optical path are withdrawn
+ connectivity.getRealizingLinks().forEach(l -> {
+ Intent intent = intentService.getIntent(l.realizingIntentKey());
+ intentService.withdraw(intent);
+ });
+
+ return connectivity != null;
+ }
+
+ @Override
+ public List<Link> getPath(MetroConnectivityId id) {
+ MetroConnectivity connectivity = connectivities.get(id);
+ if (connectivity == null) {
+ return null;
+ }
+
+ return ImmutableList.copyOf(connectivity.links());
+ }
+
+ @Override
+ public Optional<VlanId> getVlanId(MetroConnectivityId id) {
+ checkNotNull(id);
+
+ MetroConnectivity connectivity = connectivities.get(id);
+ if (connectivity != null) {
+ return getVlanTag(connectivity.links());
+ }
+
+ return Optional.empty();
+ }
+
+ /**
+ * Returns list of (optical, packet) pairs of cross connection points of missing optical path sections.
+ *
+ * Scans the given multi-layer path and looks for sections that use cross connect links.
+ * The ingress and egress points in the optical layer are combined to the packet layer ports, and
+ * are returned in a list.
+ *
+ * @param path the multi-layer path
+ * @return List of cross connect link's (packet port, optical port) pairs
+ */
+ private List<Pair<ConnectPoint, ConnectPoint>> getCrossConnectPoints(Path path) {
+ List<Pair<ConnectPoint, ConnectPoint>> xcPointPairs = new LinkedList<>();
+ boolean scanning = false;
+
+ for (Link link : path.links()) {
+ if (!isCrossConnectLink(link)) {
+ continue;
+ }
+
+ if (scanning) {
+ // link.src() is packet, link.dst() is optical
+ xcPointPairs.add(Pair.of(checkNotNull(link.src()), checkNotNull(link.dst())));
+ scanning = false;
+ } else {
+ // link.src() is optical, link.dst() is packet
+ xcPointPairs.add(Pair.of(checkNotNull(link.dst()), checkNotNull(link.src())));
+ scanning = true;
+ }
+ }
+
+ return xcPointPairs;
+ }
+
+ /**
+ * Checks if optical cross connect points are of same type.
+ *
+ * @param xcPointPairs list of cross connection points
+ * @return true if cross connect point pairs are of same type, false otherwise
+ */
+ private boolean checkXcPoints(List<Pair<ConnectPoint, ConnectPoint>> xcPointPairs) {
+ checkArgument(xcPointPairs.size() % 2 == 0);
+
+ Iterator<Pair<ConnectPoint, ConnectPoint>> itr = xcPointPairs.iterator();
+
+ while (itr.hasNext()) {
+ // checkArgument at start ensures we'll always have pairs of connect points
+ Pair<ConnectPoint, ConnectPoint> src = itr.next();
+ Pair<ConnectPoint, ConnectPoint> dst = itr.next();
+
+ Device.Type srcType = deviceService.getDevice(src.getKey().deviceId()).type();
+ Device.Type dstType = deviceService.getDevice(dst.getKey().deviceId()).type();
+
+ // Only support connections between identical port types
+ if (srcType != dstType) {
+ log.warn("Unsupported mix of cross connect points");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Scans the list of cross connection points and returns a list of optical connectivity intents.
+ * During the process, store intent ID and its realizing link information to given connectivity object.
+ *
+ * @param xcPointPairs list of cross connection points
+ * @return list of optical connectivity intents
+ */
+ private List<Intent> createIntents(List<Pair<ConnectPoint, ConnectPoint>> xcPointPairs,
+ MetroConnectivity connectivity) {
+ checkArgument(xcPointPairs.size() % 2 == 0);
+
+ List<Intent> intents = new LinkedList<>();
+ Iterator<Pair<ConnectPoint, ConnectPoint>> itr = xcPointPairs.iterator();
+
+ while (itr.hasNext()) {
+ // checkArgument at start ensures we'll always have pairs of connect points
+ Pair<ConnectPoint, ConnectPoint> src = itr.next();
+ Pair<ConnectPoint, ConnectPoint> dst = itr.next();
+
+ Port srcPort = deviceService.getPort(src.getKey().deviceId(), src.getKey().port());
+ Port dstPort = deviceService.getPort(dst.getKey().deviceId(), dst.getKey().port());
+
+ if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
+ // Create OTN circuit
+ OpticalCircuitIntent circuitIntent = OpticalCircuitIntent.builder()
+ .appId(appId)
+ .src(src.getKey())
+ .dst(dst.getKey())
+ .signalType(CltSignalType.CLT_10GBE)
+ .bidirectional(true)
+ .build();
+ intents.add(circuitIntent);
+ PacketLinkRealizedByOptical pLink = PacketLinkRealizedByOptical.create(src.getValue(), dst.getValue(),
+ circuitIntent);
+ connectivity.addRealizingLink(pLink);
+ linkPathMap.put(pLink, connectivity);
+ } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
+ // Create lightpath
+ // FIXME: hardcoded ODU signal type
+ OpticalConnectivityIntent opticalIntent = OpticalConnectivityIntent.builder()
+ .appId(appId)
+ .src(src.getKey())
+ .dst(dst.getKey())
+ .signalType(OduSignalType.ODU4)
+ .bidirectional(true)
+ .build();
+ intents.add(opticalIntent);
+ PacketLinkRealizedByOptical pLink = PacketLinkRealizedByOptical.create(src.getValue(), dst.getValue(),
+ opticalIntent);
+ connectivity.addRealizingLink(pLink);
+ linkPathMap.put(pLink, connectivity);
+ } else {
+ log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
+ return Collections.emptyList();
+ }
+ }
+
+ return intents;
+ }
+
+ /**
+ * Verifies if given device type is in packet layer, i.e., ROADM, OTN or ROADM_OTN device.
+ *
+ * @param type device type
+ * @return true if in packet layer, false otherwise
+ */
+ private boolean isPacketLayer(Device.Type type) {
+ return type == Device.Type.SWITCH || type == Device.Type.ROUTER || type == Device.Type.VIRTUAL;
+ }
+
+ /**
+ * Verifies if given device type is in packet layer, i.e., switch or router device.
+ *
+ * @param type device type
+ * @return true if in packet layer, false otherwise
+ */
+ private boolean isTransportLayer(Device.Type type) {
+ return type == Device.Type.ROADM || type == Device.Type.OTN || type == Device.Type.ROADM_OTN;
+ }
+
+ /**
+ * Verifies if given link forms a cross-connection between packet and optical layer.
+ *
+ * @param link the link
+ * @return true if the link is a cross-connect link, false otherwise
+ */
+ private boolean isCrossConnectLink(Link link) {
+ if (link.type() != Link.Type.OPTICAL) {
+ return false;
+ }
+
+ Device.Type src = deviceService.getDevice(link.src().deviceId()).type();
+ Device.Type dst = deviceService.getDevice(link.dst().deviceId()).type();
+
+ return src != dst &&
+ ((isPacketLayer(src) && isTransportLayer(dst)) || (isPacketLayer(dst) && isTransportLayer(src)));
+ }
+
+ /**
+ * Updates bandwidth resource of given connect point.
+ * @param cp Connect point
+ * @param bandwidth New bandwidth
+ */
+ private void updatePortBandwidth(ConnectPoint cp, Bandwidth bandwidth) {
+ NodeId localNode = clusterService.getLocalNode().id();
+ NodeId sourceMaster = mastershipService.getMasterFor(cp.deviceId());
+ if (localNode.equals(sourceMaster)) {
+ log.debug("update Port {} Bandwidth {}", cp, bandwidth);
+ BandwidthCapacity bwCapacity = networkConfigService.addConfig(cp, BandwidthCapacity.class);
+ bwCapacity.capacity(bandwidth).apply();
+ }
+ }
+
+ /**
+ * Updates usage information of bandwidth based on connectivity which is established.
+ * @param connectivity Metro connectivity
+ */
+ private void updateBandwidthUsage(MetroConnectivity connectivity) {
+ IntentId intentId = connectivity.getIntentId();
+ if (intentId == null) {
+ return;
+ }
+
+ List<Link> links = connectivity.links();
+
+ List<Resource> resources = links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
+ .filter(cp -> !isTransportLayer(deviceService.getDevice(cp.deviceId()).type()))
+ .map(cp -> Resources.continuous(cp.deviceId(), cp.port(),
+ Bandwidth.class).resource(connectivity.bandwidth().bps()))
+ .collect(Collectors.toList());
+
+ log.debug("allocating bandwidth for {} : {}", connectivity.getIntentId(), resources);
+ List<ResourceAllocation> allocations = resourceService.allocate(intentId, resources);
+ if (allocations.isEmpty()) {
+ log.warn("Failed to allocate bandwidth {} to {}",
+ connectivity.bandwidth().bps(), resources);
+ // TODO any recovery?
+ }
+ log.debug("Done allocating bandwidth for {}", connectivity.getIntentId());
+ }
+
+ /**
+ * Release bandwidth allocated by given connectivity.
+ * @param connectivity Metro connectivity
+ */
+ private void releaseBandwidthUsage(MetroConnectivity connectivity) {
+ IntentId intentId = connectivity.getIntentId();
+ if (intentId == null) {
+ return;
+ }
+
+ log.debug("releasing bandwidth allocated to {}", connectivity.getIntentId());
+ if (!resourceService.release(connectivity.getIntentId())) {
+ log.warn("Failed to release bandwidth allocated to {}",
+ connectivity.getIntentId());
+ // TODO any recovery?
+ }
+ log.debug("DONE releasing bandwidth for {}", connectivity.getIntentId());
+ }
+
+ /**
+ * Returns VLAN tag assigned to given path.
+ * @param links Path
+ * @return VLAN tag if found any. empty if not found.
+ */
+ private Optional<VlanId> getVlanTag(List<Link> links) {
+ Optional<ConnectPoint> edge = links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
+ .filter(portVlanMap::containsKey)
+ .findAny();
+
+ if (edge.isPresent()) {
+ return Optional.of(portVlanMap.get(edge.get()));
+ }
+ return Optional.empty();
+ }
+
+ private class BandwidthLinkWeight implements LinkWeight {
+ private Bandwidth bandwidth = null;
+
+ public BandwidthLinkWeight(Bandwidth bandwidth) {
+ this.bandwidth = bandwidth;
+ }
+
+ @Override
+ public double weight(TopologyEdge edge) {
+ Link l = edge.link();
+
+ // Ignore inactive links
+ if (l.state() == Link.State.INACTIVE) {
+ return -1.0;
+ }
+
+ // Ignore cross connect links with used ports
+ if (isCrossConnectLink(l) && usedCrossConnectLinks.contains(l)) {
+ return -1.0;
+ }
+
+ // Check availability of bandwidth
+ if (bandwidth != null) {
+ if (hasEnoughBandwidth(l.src()) && hasEnoughBandwidth(l.dst())) {
+ return 1.0;
+ } else {
+ return -1.0;
+ }
+ } else {
+ // TODO needs to differentiate optical and packet?
+ if (l.type() == Link.Type.OPTICAL) {
+ // Transport links
+ return 1.0;
+ } else {
+ // Packet links
+ return 1.0;
+ }
+ }
+ }
+
+ private boolean hasEnoughBandwidth(ConnectPoint cp) {
+ if (cp.elementId() instanceof DeviceId) {
+ Device.Type type = deviceService.getDevice(cp.deviceId()).type();
+ if (isTransportLayer(type)) {
+ // Optical ports are assumed to have enough bandwidth
+ // TODO should look up physical limit?
+ return true;
+ }
+
+ ContinuousResource resource = Resources.continuous(cp.deviceId(), cp.port(), Bandwidth.class)
+ .resource(bandwidth.bps());
+
+ return resourceService.isAvailable(resource);
+ }
+ return false;
+ }
+ }
+
+
+ public class InternalIntentListener implements IntentListener {
+ @Override
+ public void event(IntentEvent event) {
+ switch (event.type()) {
+ case INSTALLED:
+ log.info("Intent {} installed.", event.subject());
+ updateCrossConnectLink(event.subject());
+ break;
+ case WITHDRAWN:
+ log.info("Intent {} withdrawn.", event.subject());
+ removeCrossConnectLinks(event.subject());
+ break;
+ case FAILED:
+ log.info("Intent {} failed.", event.subject());
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void updateCrossConnectLink(Intent intent) {
+ linkPathMap.entrySet().stream()
+ .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
+ .forEach(e -> {
+ ConnectPoint packetSrc = e.getKey().src();
+ ConnectPoint packetDst = e.getKey().dst();
+ Bandwidth bw = e.getKey().bandwidth();
+ // Updates bandwidth of packet ports
+ updatePortBandwidth(packetSrc, bw);
+ updatePortBandwidth(packetDst, bw);
+
+ MetroConnectivity connectivity = e.getValue();
+ connectivity.setLinkEstablished(packetSrc, packetDst);
+
+ if (e.getValue().isAllRealizingLinkEstablished()) {
+ updateBandwidthUsage(connectivity);
+
+ // Notifies listeners if all links are established
+ post(new MetroPathEvent(MetroPathEvent.Type.PATH_INSTALLED, e.getValue().id()));
+ }
+ });
+ }
+
+ private void removeCrossConnectLinks(Intent intent) {
+ ConnectPoint src, dst;
+
+ if (intent instanceof OpticalCircuitIntent) {
+ OpticalCircuitIntent circuit = (OpticalCircuitIntent) intent;
+ src = circuit.getSrc();
+ dst = circuit.getDst();
+ } else if (intent instanceof OpticalConnectivityIntent) {
+ OpticalConnectivityIntent conn = (OpticalConnectivityIntent) intent;
+ src = conn.getSrc();
+ dst = conn.getDst();
+ } else {
+ return;
+ }
+
+ removeXcLinkUsage(src);
+ removeXcLinkUsage(dst);
+
+ // Set bandwidth of 0 to cross connect ports
+ Bandwidth bw = Bandwidth.bps(0);
+ linkPathMap.entrySet().stream()
+ .filter(e -> e.getKey().realizingIntentKey().equals(intent.key()))
+ .forEach(e -> {
+ ConnectPoint packetSrc = e.getKey().src();
+ ConnectPoint packetDst = e.getKey().dst();
+ // Updates bandwidth of packet ports
+ updatePortBandwidth(packetSrc, bw);
+ updatePortBandwidth(packetDst, bw);
+ MetroConnectivity connectivity = e.getValue();
+ connectivity.setLinkRemoved(packetSrc, packetDst);
+
+ // Notifies listeners if all links are gone
+ if (e.getValue().isAllRealizingLinkNotEstablished()) {
+ releaseBandwidthUsage(connectivity);
+ post(new MetroPathEvent(MetroPathEvent.Type.PATH_REMOVED, e.getValue().id()));
+ }
+ });
+ }
+
+ private void removeXcLinkUsage(ConnectPoint cp) {
+ Optional<Link> link = linkService.getLinks(cp).stream()
+ .filter(usedCrossConnectLinks::contains)
+ .findAny();
+
+ if (!link.isPresent()) {
+ log.warn("Cross connect point {} has no cross connect link.", cp);
+ return;
+ }
+
+ usedCrossConnectLinks.remove(link.get());
+ }
+ }
+
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if (event.configClass() != CeVlanConfig.class) {
+ return;
+ }
+
+ ConnectPoint cp = (ConnectPoint) event.subject();
+ CeVlanConfig config = networkConfigService.getConfig(cp, CeVlanConfig.class);
+ if (config != null && config.ceVlanId().isPresent()) {
+ log.info("VLAN tag {} is assigned to port {}", config.ceVlanId().get(), cp);
+ portVlanMap.put(cp, config.ceVlanId().get());
+ } else {
+ portVlanMap.remove(cp);
+ }
+ }
+
+ }
+}
+
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/PacketLinkRealizedByOptical.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/PacketLinkRealizedByOptical.java
new file mode 100644
index 0000000..9159b96
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/PacketLinkRealizedByOptical.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ecord.metro;
+
+import org.onlab.util.Bandwidth;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.OpticalCircuitIntent;
+import org.onosproject.net.intent.OpticalConnectivityIntent;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Entity to represent packet link realized by optical intent.
+ */
+public class PacketLinkRealizedByOptical {
+ private final ConnectPoint src, dst;
+ private final Bandwidth bandwidth;
+ // TODO should be list of Intent Key?
+ private final Key realizingIntentKey;
+ // established=false represents that this (packet) link is expected to be
+ // discovered after underlying (optical) path has been provisioned.
+ private boolean established;
+
+ public PacketLinkRealizedByOptical(ConnectPoint src, ConnectPoint dst,
+ Key realizingIntentKey, Bandwidth bandwidth) {
+ this.src = src;
+ this.dst = dst;
+ this.realizingIntentKey = realizingIntentKey;
+ this.bandwidth = bandwidth;
+ this.established = false;
+ }
+
+ public static PacketLinkRealizedByOptical create(ConnectPoint src, ConnectPoint dst,
+ OpticalCircuitIntent intent) {
+ checkNotNull(src);
+ checkNotNull(dst);
+ checkNotNull(intent);
+
+ long rate = intent.getSignalType().bitRate();
+ return new PacketLinkRealizedByOptical(src, dst, intent.key(), Bandwidth.bps(rate));
+ }
+
+ public static PacketLinkRealizedByOptical create(ConnectPoint src, ConnectPoint dst,
+ OpticalConnectivityIntent intent) {
+ checkNotNull(src);
+ checkNotNull(dst);
+ checkNotNull(intent);
+
+ long rate = intent.getSignalType().bitRate();
+ return new PacketLinkRealizedByOptical(src, dst, intent.key(), Bandwidth.bps(rate));
+ }
+
+ public ConnectPoint src() {
+ return src;
+ }
+
+ public ConnectPoint dst() {
+ return dst;
+ }
+
+ public Bandwidth bandwidth() {
+ return bandwidth;
+ }
+
+ public Key realizingIntentKey() {
+ return realizingIntentKey;
+ }
+
+ public boolean isEstablished() {
+ return established;
+ }
+
+ public void setEstablished(boolean established) {
+ this.established = established;
+ }
+
+ public boolean equals(ConnectPoint src, ConnectPoint dst) {
+ return (this.src.equals(src) && this.dst.equals(dst));
+ }
+
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroConnectivityId.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroConnectivityId.java
new file mode 100644
index 0000000..6c02464
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroConnectivityId.java
@@ -0,0 +1,65 @@
+/*
+ * 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.ecord.metro.api;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * ID for metro connectivity request.
+ */
+public final class MetroConnectivityId {
+ private final long value;
+
+ public static MetroConnectivityId valueOf(long value) {
+ return new MetroConnectivityId(value);
+ }
+
+ MetroConnectivityId(long value) {
+ this.value = value;
+ }
+
+ public long value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ MetroConnectivityId that = (MetroConnectivityId) o;
+
+ return value == that.value;
+
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) (value ^ (value >>> 32));
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("value", value)
+ .toString();
+ }
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathEvent.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathEvent.java
new file mode 100644
index 0000000..4a5f38a
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathEvent.java
@@ -0,0 +1,33 @@
+/*
+ * 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.ecord.metro.api;
+
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Event related to metro domain path setup.
+ */
+public class MetroPathEvent extends AbstractEvent<MetroPathEvent.Type, MetroConnectivityId> {
+ public enum Type {
+ PATH_INSTALLED,
+ PATH_REMOVED
+ }
+
+ public MetroPathEvent(Type type, MetroConnectivityId subject) {
+ super(type, subject);
+ }
+
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathListener.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathListener.java
new file mode 100644
index 0000000..9d8e894
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathListener.java
@@ -0,0 +1,24 @@
+/*
+ * 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.ecord.metro.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Entity capable of receiving metro path related events.
+ */
+public interface MetroPathListener extends EventListener<MetroPathEvent> {
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathService.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathService.java
new file mode 100644
index 0000000..2377b7f
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/MetroPathService.java
@@ -0,0 +1,75 @@
+/*
+ * 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.ecord.metro.api;
+
+import com.google.common.annotations.Beta;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Bandwidth;
+import org.onosproject.event.ListenerService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Service to setup metro domain connectivity.
+ */
+@Beta
+public interface MetroPathService extends ListenerService<MetroPathEvent, MetroPathListener> {
+
+ /**
+ * Calculates optical path between connect points and sets up connectivity.
+ *
+ * @param ingress ingress port
+ * @param egress egress port
+ * @param bandwidth required bandwidth. No bandwidth is assured if null.
+ * @param latency required latency. No latency is assured if null.
+ * @return ID of created connectivity if successful. null otherwise.
+ */
+ MetroConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
+ Bandwidth bandwidth, Duration latency);
+
+ /**
+ * Sets up connectivity along given optical path.
+ *
+ * @param path path along which connectivity will be set up
+ * @param bandwidth required bandwidth. No bandwidth is assured if null.
+ * @param latency required latency. No latency is assured if null.
+ * @return true if successful. false otherwise.
+ */
+ MetroConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency);
+
+ /**
+ * Removes connectivity with given ID.
+ *
+ * @param id ID of connectivity
+ * @return true if succeed. false if failed.
+ */
+ boolean removeConnectivity(MetroConnectivityId id);
+
+ /**
+ * Returns path assigned to given ID.
+ * @param id ID of connectivity
+ * @return list of link that compose a path. null if ID is invalid.
+ */
+ List<Link> getPath(MetroConnectivityId id);
+
+ // FIXME This is for ONS2016 demo use only
+ Optional<VlanId> getVlanId(MetroConnectivityId id);
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/package-info.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/package-info.java
new file mode 100644
index 0000000..866eb9b
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/api/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+/**
+ * API to access E-CORD metro service.
+ */
+package org.onosproject.ecord.metro.api;
\ No newline at end of file
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/AddMetroConnectivityCommand.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/AddMetroConnectivityCommand.java
new file mode 100644
index 0000000..05fc378
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/AddMetroConnectivityCommand.java
@@ -0,0 +1,93 @@
+/*
+ * 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.ecord.metro.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Bandwidth;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.ecord.metro.api.MetroConnectivityId;
+import org.onosproject.ecord.metro.api.MetroPathService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import java.util.Optional;
+
+@Command(scope = "onos", name = "add-metro-connectivity",
+ description = "Configure metro domain connectivity")
+public class AddMetroConnectivityCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "ingress", description = "Ingress connect point",
+ required = true, multiValued = false)
+ String ingressStr = null;
+
+ @Argument(index = 1, name = "egress", description = "Egress connect point",
+ required = true, multiValued = false)
+ String egressStr = null;
+
+ @Argument(index = 2, name = "bandwidth", description = "Bandwidth",
+ required = false, multiValued = false)
+ String bandwidthStr = null;
+
+ @Argument(index = 3, name = "latency", description = "Latency",
+ required = true, multiValued = false)
+ String latencyStr = null;
+
+
+ @Override
+ protected void execute() {
+ MetroPathService metroPathService = get(MetroPathService.class);
+
+ ConnectPoint ingress = readConnectPoint(ingressStr);
+ ConnectPoint egress = readConnectPoint(egressStr);
+ if (ingress == null || egress == null) {
+ print("Invalid connect points: %s, %s", ingressStr, egressStr);
+ return;
+ }
+
+ Bandwidth bandwidth = (bandwidthStr == null || bandwidthStr.isEmpty()) ? null :
+ Bandwidth.bps(Long.valueOf(bandwidthStr));
+
+ print("Trying to setup connectivity between %s and %s.", ingress, egress);
+ MetroConnectivityId id = metroPathService.setupConnectivity(ingress, egress, bandwidth, null);
+ if (id == null) {
+ print("Failed.");
+ return;
+ }
+ print("Metro path ID : %s", id.value());
+ Optional<VlanId> vlanId = metroPathService.getVlanId(id);
+ if (vlanId.isPresent()) {
+ print(" -- VLAN ID %d was assigned.", vlanId.get().toShort());
+ } else {
+ print(" -- No VLAN ID was assigned.");
+ }
+ }
+
+ private ConnectPoint readConnectPoint(String str) {
+ String[] strings = str.split("/");
+ if (strings.length != 2) {
+ return null;
+ }
+
+ DeviceId devId = DeviceId.deviceId(strings[0]);
+ PortNumber port = PortNumber.portNumber(strings[1]);
+
+ return new ConnectPoint(devId, port);
+ }
+
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/RemoveMetroConnectivityCommand.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/RemoveMetroConnectivityCommand.java
new file mode 100644
index 0000000..ed6d2c8
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/RemoveMetroConnectivityCommand.java
@@ -0,0 +1,45 @@
+/*
+ * 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.ecord.metro.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.ecord.metro.api.MetroConnectivityId;
+import org.onosproject.ecord.metro.api.MetroPathService;
+
+@Command(scope = "onos", name = "remove-metro-connectivity",
+ description = "Remove metro domain connectivity")
+public class RemoveMetroConnectivityCommand extends AbstractShellCommand {
+ @Argument(index = 0, name = "id", description = "ID of metro connectivity",
+ required = true, multiValued = false)
+ String idStr = null;
+
+ @Override
+ protected void execute() {
+ MetroPathService metroPathService = get(MetroPathService.class);
+
+ MetroConnectivityId id = MetroConnectivityId.valueOf(Long.valueOf(idStr));
+
+ print("Trying to remove connectivity with id %s.", idStr);
+ if (metroPathService.removeConnectivity(id)) {
+ print(" -- success");
+ } else {
+ print(" -- failed");
+ }
+
+ }
+}
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/package-info.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/package-info.java
new file mode 100644
index 0000000..8907956
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/cli/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+/**
+ * CLI to control E-CORD metro service.
+ */
+package org.onosproject.ecord.metro.cli;
\ No newline at end of file
diff --git a/ecord/metro/src/main/java/org/onosproject/ecord/metro/package-info.java b/ecord/metro/src/main/java/org/onosproject/ecord/metro/package-info.java
new file mode 100644
index 0000000..e197e5c
--- /dev/null
+++ b/ecord/metro/src/main/java/org/onosproject/ecord/metro/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.
+ */
+
+/**
+ * Module to control E-CORD metro domain.
+ */
+package org.onosproject.ecord.metro;
\ No newline at end of file
diff --git a/ecord/metro/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/ecord/metro/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..df45cf5
--- /dev/null
+++ b/ecord/metro/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright 2016 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.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.ecord.metro.cli.AddMetroConnectivityCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.ecord.metro.cli.RemoveMetroConnectivityCommand"/>
+ </command>
+ </command-bundle>
+
+</blueprint>
diff --git a/ecord/metro/src/main/resources/config-examples/network-cfg.json b/ecord/metro/src/main/resources/config-examples/network-cfg.json
new file mode 100644
index 0000000..d5da00d
--- /dev/null
+++ b/ecord/metro/src/main/resources/config-examples/network-cfg.json
@@ -0,0 +1,14 @@
+{
+ "ports": {
+ "of:0000ffffffffff01/10": {
+ "ceVlan": {
+ "tag" : 100
+ }
+ },
+ "of:0000ffffffffff01/11": {
+ "ceVlan": {
+ "tag" : 200
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 09d2932..aa196fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
<module>tvue</module>
<module>uiref</module>
<module>ecord/co</module>
+ <module>ecord/metro</module>
</modules>
<properties>