Application for installing MEF services in ONOS
Modified CarrierEthernetService.Type to match updated mef-sca-api app.
Change-Id: I3e1fe1ccb51b26a4b2682ad69e93abc19b60fb4a
diff --git a/ecord/carrierethernet/pom.xml b/ecord/carrierethernet/pom.xml
new file mode 100644
index 0000000..516f264
--- /dev/null
+++ b/ecord/carrierethernet/pom.xml
@@ -0,0 +1,179 @@
+<?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>
+
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-carrierethernet</artifactId>
+ <version>1.6.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <description>Application for installing MEF services in ONOS</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.carrierethernet</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-cli</artifactId>
+ <version>${onos.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-drivers</artifactId>
+ <version>${onos.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-ecord-metro</artifactId>
+ <version>${onos.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <version>3.0.3</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</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.onosproject</groupId>
+ <artifactId>openflowj</artifactId>
+ <version>0.9.3.onos-SNAPSHOT</version>
+ <scope>provided</scope>
+ </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/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetBandwidthProfile.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetBandwidthProfile.java
new file mode 100644
index 0000000..9dfe5f4
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetBandwidthProfile.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.app;
+
+import org.onlab.util.Bandwidth;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Bandwidth profile for a CE UNI.
+ */
+public class CarrierEthernetBandwidthProfile {
+
+ public enum Type {
+ INTERFACE, EVC, COS
+ }
+
+ protected String id;
+ protected String cfgId;
+ protected Type type;
+ protected Bandwidth cir;
+ protected Bandwidth eir;
+ protected long cbs;
+ protected long ebs;
+
+ public CarrierEthernetBandwidthProfile(String id, String cfgId, Type type,
+ Bandwidth cir, Bandwidth eir, long cbs, long ebs) {
+ this.id = id;
+ this.cfgId = cfgId;
+ this.type = type;
+ this.cir = cir;
+ this.eir = eir;
+ this.cbs = cbs;
+ this.ebs = ebs;
+ }
+
+ /**
+ * Returns BWP string identifier.
+ *
+ * @return BWP string identifier
+ */
+ public String id() {
+ return id;
+ }
+
+ /**
+ * Returns BWP string config identifier.
+ *
+ * @return BWP string config identifier
+ */
+ public String cfgId() {
+ return cfgId;
+ }
+
+ /**
+ * Returns BWP type (INTERFACE, EVC, COS).
+ *
+ * @return BWP type
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns BWP CIR rate.
+ *
+ * @return BWP CIR rate
+ */
+ public Bandwidth cir() {
+ return cir;
+ }
+
+ /**
+ * Returns BWP EIR rate.
+ *
+ * @return BWP EIR rate
+ */
+ public Bandwidth eir() {
+ return eir;
+ }
+
+ /**
+ * Returns BWP CBS in Bytes.
+ *
+ * @return BWP CBS in Bytes
+ */
+ public long cbs() {
+ return cbs;
+ }
+
+ /**
+ * Returns BWP EBS in Bytes.
+ *
+ * @return BWP EBS in Bytes
+ */
+ public long ebs() {
+ return ebs;
+ }
+
+ /**
+ * Sets BWP id.
+ *
+ * @param id the id to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets BWP CIR rate.
+ *
+ * @param cir the CIR to set
+ */
+ public void setCir(Bandwidth cir) {
+ this.cir = cir;
+ }
+
+ /**
+ * Sets BWP EIR rate.
+ *
+ * @param eir the EIR to set
+ */
+ public void setEir(Bandwidth eir) {
+ this.eir = eir;
+ }
+
+ public String toString() {
+
+ return toStringHelper(this)
+ .add("id", id)
+ .add("type", type)
+ .add("cir", cir)
+ .add("cbs", cbs)
+ .add("eir", eir)
+ .add("ebs", ebs).toString();
+ }
+
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetManager.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetManager.java
new file mode 100644
index 0000000..7e0b36c
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetManager.java
@@ -0,0 +1,563 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.app;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.VlanId;
+
+import org.onlab.util.Bandwidth;
+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.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.LinkService;
+import org.slf4j.Logger;
+
+import java.time.Duration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Component(immediate = true)
+@Service(value = CarrierEthernetManager.class)
+public class CarrierEthernetManager {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MetroPathService metroPathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CarrierEthernetPacketProvisioner cePktProvisioner;
+
+ private MetroPathListener metroEventListener = new MetroEventListener();
+
+ // Keeps track of the next S-VLAN tag the app will try to use
+ private static short curVlanId = 0;
+
+ private static final int METRO_CONNECT_TIMEOUT_MILLIS = 7000;
+
+ private static final boolean PACKET_OPTICAL_TOPO = true;
+
+ // TODO: Implement distributed store for CE services
+ // The installed CE services
+ private final Map<String, CarrierEthernetService> serviceMap = new ConcurrentHashMap<>();
+
+ // TODO: Refactor this part
+ private final Map<MetroConnectivityId, MetroPathEvent.Type> metroConnectStatusMap = new ConcurrentHashMap<>();
+
+ // TODO: Implement distributed store for CE UNIs
+ // The installed CE UNIs
+ private final Map<String, CarrierEthernetUni> uniMap = new ConcurrentHashMap<>();
+
+ /**
+ * Activate this component.
+ */
+ @Activate
+ public void activate() {
+ metroPathService.addListener(metroEventListener);
+ }
+
+ /**
+ * Deactivate this component.
+ */
+ @Deactivate
+ public void deactivate() {
+ removeAllServices();
+ metroPathService.removeListener(metroEventListener);
+ }
+
+ /**
+ * Returns the map of installed services.
+ *
+ * @return map of installed services
+ */
+ public Map<String, CarrierEthernetService> serviceMap() {
+ return this.serviceMap;
+ }
+
+ // TODO: Add method to remove a UNI from an already installed service
+
+ /**
+ * Get an installed CE using its id.
+ *
+ * @return the CE service definition or null if the service doesn't exist
+ */
+ public CarrierEthernetService getService(String serviceId) {
+ return ((serviceMap.get(serviceId) == null) ? null : serviceMap.get(serviceId));
+ }
+
+ /**
+ * Get the map containing all installed services
+ *
+ * @return the CE service map
+ */
+ public Map<String, CarrierEthernetService> getServiceMap() {
+ return serviceMap;
+ }
+
+ /**
+ * Get the map containing all global UNIs
+ *
+ * @return the global UNI map
+ */
+ public Map<String, CarrierEthernetUni> getUniMap() {
+ return uniMap;
+ }
+
+ /**
+ * Verify the validity of a CE service definition taking also into account current network status.
+ *
+ * @param originalService the provided CE service definition
+ * @return a valid, potentially modified service description, or null if the service could not be validated
+ */
+ private CarrierEthernetService validateService(CarrierEthernetService originalService) {
+
+ // Make a copy of the provided service, since it may be modified
+ CarrierEthernetService service = originalService;
+
+ // Try to set a unique VLAN id for the service unless the service is being updated
+ // TODO: Add different connectivity types
+ if (service.vlanId() == null) {
+ service.setVlanId(generateVlanId());
+ if (service.vlanId() == null) {
+ log.error("No available VLAN id found.");
+ return null;
+ }
+ }
+
+ // Verify that CE-VLAN ID is provided to either all UNIs or none and set the virtualService flag accordingly
+ boolean isVirtual = false;
+ Iterator<CarrierEthernetUni> it = service.uniSet().iterator();
+ while (it.hasNext()) {
+ CarrierEthernetUni uni = it.next();
+ if (uni.ceVlanId() == null && isVirtual) {
+ log.error("Could not validate the virtual status of the service.");
+ return null;
+ } else if (uni.ceVlanId() != null){
+ isVirtual = true;
+ }
+ }
+ service.setIsVirtual(isVirtual);
+
+ // Set unique id for the service unless the service is being updated
+ if (service.id() == null) {
+ service.setId(generateServiceId(service));
+ }
+
+ Set<CarrierEthernetUni> validatedUniSet = new HashSet<>();
+
+ // Check the UNIs of the service, possibly removing UNIs that are incompatible with existing ones
+ it = service.uniSet().iterator();
+ while (it.hasNext()) {
+ CarrierEthernetUni uni = it.next();
+ // Change the name of the UNI's BWP to the service name if it is an EVC BWP
+ if (uni.bwp().type().equals(CarrierEthernetBandwidthProfile.Type.EVC)) {
+ uni.bwp().setId(service.id());
+ }
+ // Check first if UNI already exists by checking against the global UNI Map
+ if (uniMap.keySet().contains(uni.id())) {
+ CarrierEthernetUni existingUni = uniMap.get(uni.id());
+ // Check if the service-specific UNI is compatible with the global one
+ if (!(existingUni.validateServiceUni(uni))) {
+ // If service is of ROOT_MULTIPOINT type and we have removed the root, return null
+ if (service.type() == CarrierEthernetService.Type.ROOT_MULTIPOINT &&
+ uni.type() == CarrierEthernetUni.Type.ROOT) {
+ log.error("Root UNI could not be added to %s service.", service.type().name());
+ return null;
+ }
+ log.warn("UNI {} could not be added to service.", uni.id());
+ continue;
+ } else {
+ // Add UNI to service description
+ validatedUniSet.add(uni);
+ }
+ } else {
+ // Add UNI to service description
+ validatedUniSet.add(uni);
+ }
+ }
+
+ // Update the service UNI set, based on the validated UNIs
+ service.setUniSet(validatedUniSet);
+
+ if (service.type().equals(CarrierEthernetService.Type.ROOT_MULTIPOINT) && (service.uniSet().size() < 2)) {
+ log.error("{} service requires at least two UNIs.", service.type().name());
+ return null;
+ }
+
+ if (service.type().equals(CarrierEthernetService.Type.MULTIPOINT_TO_MULTIPOINT) &&
+ (service.uniSet().size() < 3)) {
+ log.error("{} service requires more than two UNIs.", service.type().name());
+ return null;
+ }
+
+ if (service.type().equals(CarrierEthernetService.Type.POINT_TO_POINT) && (service.uniSet().size() != 2)) {
+ log.error("{} service requires exactly two UNIs.", service.type().name());
+ return null;
+ }
+
+ return service;
+ }
+
+ /**
+ * Establish connectivity according to the service type (E-Line, E-Tree, E-LAN) and the service parameters.
+ *
+ * @param originalService the CE service definition
+ * @return boolean value indicating whether the service could be established even partially
+ */
+ public boolean establishConnectivity(CarrierEthernetService originalService) {
+
+ // If service already exists, remove it and reestablish with new parameters
+ if (originalService.id() != null && serviceMap.containsKey(originalService.id())) {
+ return updateService(originalService);
+ } else {
+ originalService.setId(null);
+ }
+
+ CarrierEthernetService service = validateService(originalService);
+
+ boolean outcome = false;
+
+ if (service == null) {
+ log.error("Service could not be installed, please check log for details.");
+ return outcome;
+ }
+
+ // Temporary set for iterating through service UNI pairs
+ Set<CarrierEthernetUni> uniSet = service.uniSet();
+
+ // Temporary set for indicating which UNIs were finally included in the service
+ Set<CarrierEthernetUni> usedUniSet = new HashSet<>();
+
+ Iterator<CarrierEthernetUni> it1 = uniSet.iterator();
+ while (it1.hasNext()) {
+
+ CarrierEthernetUni uni1 = it1.next();
+
+ // Iterate through all the remaining UNIs
+ Iterator<CarrierEthernetUni> it2 = uniSet.iterator();
+ while (it2.hasNext()) {
+
+ CarrierEthernetUni uni2 = it2.next();
+
+ // Skip equals
+ if (uni1.equals(uni2)) {
+ continue;
+ }
+
+ // Do not establish connectivity between leaf UNIs (applies to Rooted_Multipoint)
+ if (uni1.type() == CarrierEthernetUni.Type.LEAF && uni2.type() == CarrierEthernetUni.Type.LEAF) {
+ continue;
+ }
+
+ MetroConnectivityId metroConnectId = null;
+
+ if (PACKET_OPTICAL_TOPO) {
+ metroConnectId = setupMetroConnectivity(uni1.cp(), uni2.cp(), uni1.bwp().cir(), service.latency());
+
+ if (metroConnectId == null ||
+ metroConnectStatusMap.get(metroConnectId) != MetroPathEvent.Type.PATH_INSTALLED) {
+ log.error("Could not establish metro connectivity between {} and {}" +
+ " (metro id and status: {}, {})", uni1.cp(), uni2.cp(), metroConnectId,
+ (metroConnectId == null ? "null" : metroConnectStatusMap.get(metroConnectId)));
+ //continue;
+ }
+
+ if (metroConnectId != null) {
+ service.setMetroConnectivityId(metroConnectId);
+ service.setMetroConnectivityStatus(metroConnectStatusMap.get(metroConnectId));
+ }
+
+ log.info("Metro connectivity id and status for CE service {}: {}, {}", service.id(),
+ service.metroConnectivity().id(), service.metroConnectivity().status());
+
+ // FIXME: Temporary hack for ONS: Get vlanId from metro app
+ if (metroConnectId != null) {
+ Optional<VlanId> vlanId = metroPathService.getVlanId(metroConnectId);
+ if (vlanId.isPresent()) {
+ service.setVlanId(vlanId.get());
+ }
+ }
+ }
+
+ if (!cePktProvisioner.setupConnectivity(uni1, uni2, service)) {
+ log.warn("Could not set up packet connectivity between {} and {}", uni1, uni2);
+ removeMetroConnectivity(metroConnectId);
+ continue;
+ }
+
+ // Indicate that connection for at least one UNI pair has been established
+ outcome = true;
+
+ // Add UNIs to the set of UNIs used by the service
+ usedUniSet.add(uni1);
+ usedUniSet.add(uni2);
+ }
+ // Remove UNI from temporary set so that each pair is visited only once
+ it1.remove();
+ }
+
+ // Update the service UNI set, based on the UNIs actually used
+ service.setUniSet(usedUniSet);
+
+ // If no pair was connected, do not register the service
+ if (outcome) {
+ serviceMap.put(service.id(), service);
+ cePktProvisioner.applyBandwidthProfiles(service);
+ // Apply the BWPs of the service UNI to the global UNIs, creating them if needed
+ applyBandwidthProfiles(service.uniSet());
+ }
+
+ return outcome;
+ }
+
+ /**
+ * Reestablish connectivity for an existing service.
+ *
+ * @param originalService the updated CE service definition
+ * @return boolean value indicating whether the service could be established even partially
+ */
+ public boolean updateService(CarrierEthernetService originalService) {
+ // Just checking again
+ if (serviceMap.containsKey(originalService.id())) {
+ log.info("Updating existing service {}", originalService.id());
+ // Keep the VLAN ID of the original service
+ originalService.setVlanId(serviceMap.get(originalService.id()).vlanId());
+ removeService(originalService.id());
+ }
+ return establishConnectivity(originalService);
+ }
+
+ /**
+ * Applies bandwidth profiles to the UNIs of a service and adds them if needed to the global UNI map.
+ *
+ * @param uniSet set of UNIs that are included in the service
+ */
+ private void applyBandwidthProfiles(Set<CarrierEthernetUni> uniSet) {
+
+ uniSet.forEach(uni -> {
+ if (!(uniMap.keySet().contains(uni.id()))) {
+ // Just add the UNI as it appears at the service
+ uniMap.put(uni.id(), uni);
+ } else {
+ // Add UNI resources (BWP, CE-VLAN ID) to existing global UNI
+ CarrierEthernetUni newUni = uniMap.get(uni.id());
+ newUni.addServiceUni(uni);
+ // Update config identifier
+ newUni.setCfgId(uni.cfgId());
+ uniMap.put(uni.id(), newUni);
+ }
+ });
+ }
+
+ /**
+ * Removes bandwidth profiles from the UNIs of a service and removes them if needed from the global UNI map.
+ *
+ * @param serviceId the CE service id
+ */
+ private void removeBandwidthProfiles(String serviceId) {
+
+ serviceMap.get(serviceId).uniSet().forEach(uni -> {
+ // TODO: Check if the bandwidth profile really needs to be removed (e.g. may be CoS)
+ cePktProvisioner.removeBandwidthProfiles(serviceMap.get(serviceId));
+
+ // Remove UNI resources (BWP, CE-VLAN ID) from global UNI
+ CarrierEthernetUni newUni = uniMap.get(uni.id());
+ newUni.removeServiceUni(uni);
+ uniMap.put(uni.id(), newUni);
+ });
+ }
+
+ /**
+ * Removes all resources associated with the application.
+ *
+ * This will be called either from the deactivate method or as a response to a CLI command.
+ * */
+ public void removeAllServices() {
+ serviceMap.keySet().forEach(serviceId -> {
+ CarrierEthernetService service = serviceMap.get(serviceId);
+ cePktProvisioner.removeConnectivity(service);
+ cePktProvisioner.removeBandwidthProfiles(service);
+ removeMetroConnectivity(service.metroConnectivity().id());
+ removeBandwidthProfiles(serviceId);
+ });
+ serviceMap.clear();
+ }
+
+ /**
+ * Removes all resources associated with a specific CE service.
+ *
+ * @param serviceId the CE service id
+ * */
+ public void removeService(String serviceId) {
+ if (serviceMap.containsKey(serviceId)) {
+ CarrierEthernetService service = serviceMap.get(serviceId);
+ cePktProvisioner.removeConnectivity(service);
+ cePktProvisioner.removeBandwidthProfiles(service);
+ removeMetroConnectivity(service.metroConnectivity().id());
+ removeBandwidthProfiles(serviceId);
+ serviceMap.remove(serviceId);
+ }
+ }
+
+ /**
+ * Generates a unique vlanId in the context of the CE app.
+ *
+ * @return the generated vlanId or null if none found
+ * */
+ public VlanId generateVlanId() {
+
+ Set<VlanId> vlanIdSet = new HashSet<>();
+ VlanId vlanId = null;
+
+ serviceMap.values().forEach(service -> vlanIdSet.add(service.vlanId));
+
+ // If all vlanIds are being used return null, else try to find the next available one
+ if (vlanIdSet.size() < VlanId.MAX_VLAN - 1) {
+ do {
+ curVlanId = nextValidShort(curVlanId);
+ vlanId = VlanId.vlanId(curVlanId);
+ }
+ while (vlanIdSet.contains(vlanId));
+ }
+
+ return vlanId;
+ }
+
+ private short nextValidShort(short i) {
+
+ if (i >= VlanId.MAX_VLAN || i <= 0) {
+ return 1;
+ } else {
+ return (short) (i + 1);
+ }
+ }
+
+ /**
+ * Generates a unique id in the context of the CE app.
+ *
+ * @return the generated vlanId or null if none found
+ * */
+ public String generateServiceId(CarrierEthernetService service) {
+
+ // TODO: Add different connectivity types
+
+ String tmpType;
+
+ if (service.type().equals(CarrierEthernetService.Type.POINT_TO_POINT)) {
+ tmpType = "Line";
+ } else if (service.type().equals(CarrierEthernetService.Type.MULTIPOINT_TO_MULTIPOINT)) {
+ tmpType = "LAN";
+ } else {
+ tmpType = "Tree";
+ }
+
+ String serviceId = "E" + (service.isVirtual() ? "V" : "") + "P-" + tmpType + "-" +
+ service.vlanId().toString();
+
+ return serviceId;
+ }
+
+ /**
+ * Adds all potential UNIs to the global UNI map if they are not already there.
+ *
+ * */
+ // TODO: Modify to return set of UNIs so that method can be reused in Uni Completer
+ public void addGlobalUnis() {
+
+ CarrierEthernetUni uni;
+ // Generate the device ID/port number identifiers
+ for (Device device : deviceService.getDevices()) {
+ for (Port port : deviceService.getPorts(device.id())) {
+ // Consider only physical ports which are currently active
+ if (!port.number().isLogical() && port.isEnabled()) {
+ String cpString = device.id().toString() + "/" + port.number();
+ ConnectPoint cp = ConnectPoint.deviceConnectPoint(cpString);
+ // Add the UNI associated with generated connect point only if it doesn't belong to any link
+ // and only if it belongs to a packet switch
+ if (linkService.getEgressLinks(cp).isEmpty() && linkService.getIngressLinks(cp).isEmpty() &&
+ device.type().equals(Device.Type.SWITCH)) {
+ uni = new CarrierEthernetUni(cp, cpString, null, null, null);
+ // Add UNI only if it's not already there
+ if (!uniMap.containsKey(uni.id())) {
+ uniMap.put(uni.id(), uni);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private class MetroEventListener implements MetroPathListener {
+
+ @Override
+ public void event(MetroPathEvent event) {
+ switch (event.type()) {
+ case PATH_INSTALLED: case PATH_REMOVED:
+ log.info("Metro path event {} received for {}.", event.type(), event.subject());
+ metroConnectStatusMap.put(event.subject(), event.type());
+ break;
+ default:
+ log.error("Unexpected metro event type.");
+ break;
+ }
+ }
+ }
+
+ private MetroConnectivityId setupMetroConnectivity(ConnectPoint ingress, ConnectPoint egress,
+ Bandwidth bandwidth, Duration latency) {
+ MetroConnectivityId metroConnectId = metroPathService.setupConnectivity(ingress, egress, bandwidth, latency);
+ if (metroConnectId != null) {
+ long startTime = System.currentTimeMillis();
+ while (((System.currentTimeMillis() - startTime) < METRO_CONNECT_TIMEOUT_MILLIS) &&
+ (metroConnectStatusMap.get(metroConnectId) != MetroPathEvent.Type.PATH_INSTALLED)) {
+ }
+ }
+ return metroConnectId;
+ }
+
+ private void removeMetroConnectivity(MetroConnectivityId metroConnectId) {
+ if (metroConnectId != null) {
+ metroPathService.removeConnectivity(metroConnectId);
+ long startTime = System.currentTimeMillis();
+ while (((System.currentTimeMillis() - startTime) < METRO_CONNECT_TIMEOUT_MILLIS) &&
+ (metroConnectStatusMap.get(metroConnectId) != MetroPathEvent.Type.PATH_REMOVED)) {
+ }
+ }
+ }
+
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetOpenFlowPacketNodeManager.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetOpenFlowPacketNodeManager.java
new file mode 100644
index 0000000..92dd5f9
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetOpenFlowPacketNodeManager.java
@@ -0,0 +1,844 @@
+package org.onosproject.ecord.carrierethernet.app;
+
+import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.driver.extensions.OfdpaMatchVlanVid;
+import org.onosproject.driver.extensions.OfdpaSetVlanVid;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.DefaultBand;
+import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.meter.DefaultMeterRequest;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.slf4j.Logger;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Collection;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Class used to control Carrier Ethernet nodes according to the OpenFlow protocol.
+ */
+@Component(immediate = true)
+@Service (value = CarrierEthernetOpenFlowPacketNodeManager.class)
+public class CarrierEthernetOpenFlowPacketNodeManager extends CarrierEthernetPacketNodeManager {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupService groupService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MeterService meterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenFlowController controller;
+
+ private final Logger log = getLogger(getClass());
+
+ private static ApplicationId appId;
+
+ private static final int PRIORITY = 50000;
+
+ // TODO: Get the OFDPA2.0 table numbers from the OFDPA Pipeline Class?
+ protected static final int PORT_TABLE = 0;
+ protected static final int VLAN_TABLE = 10;
+ protected static final int TMAC_TABLE = 20;
+ protected static final int UNICAST_ROUTING_TABLE = 30;
+ protected static final int MULTICAST_ROUTING_TABLE = 40;
+ protected static final int MPLS_TABLE_0 = 23;
+ protected static final int MPLS_TABLE_1 = 24;
+ protected static final int BRIDGING_TABLE = 50;
+ protected static final int ACL_TABLE = 60;
+ protected static final int MAC_LEARNING_TABLE = 254;
+
+ // TODO: Below maps to be replaced by the meter ids, egress cps and flow rules kept with each service (?)
+ private final Map<String, Set<DeviceMeterId>> deviceMeterIdMap = new HashMap<>();
+ private final Map<String, Set<ConnectPoint>> egressCpMap = new HashMap<>();
+ private final Map<String, Set<FlowRule>> flowRuleMap = new HashMap();
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication("org.onosproject.ecord.carrierethernet");
+ }
+
+ @Deactivate
+ protected void deactivate() {}
+
+ @Override
+ public void setNodeForwarding(CarrierEthernetService service, CarrierEthernetUni srcUni, CarrierEthernetUni dstUni,
+ ConnectPoint ingress, ConnectPoint egress, boolean first, boolean last) {
+
+ // TODO: Produce error if ingress and egress do not belong to same device
+
+ Set<ConnectPoint> egressCpSet = egressCpMap.get(service.id());
+ if (egressCpSet == null) {
+ egressCpSet = new HashSet<>();
+ }
+ Set<FlowRule> flowRuleSet = flowRuleMap.get(service.id());
+ if (flowRuleSet == null) {
+ flowRuleSet = new HashSet<>();
+ }
+
+ flowRuleSet.addAll(createFlowRules(service.id(), srcUni.ceVlanId(), service.vlanId(),
+ ingress, egress, first, last));
+
+ egressCpSet.add(egress);
+
+ egressCpMap.put(service.id(), egressCpSet);
+ flowRuleMap.put(service.id(), flowRuleSet);
+
+ }
+
+ // FIXME: Temporary solution for establishing flow rules according to switch type
+ private Set<FlowRule> createFlowRules(String serviceId, VlanId ceVlanId, VlanId vlanId,
+ ConnectPoint ingress, ConnectPoint egress, boolean first, boolean last) {
+
+ Dpid dpid = Dpid.dpid(egress.deviceId().uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+
+ Set<FlowRule> flowRuleSet = new HashSet<>();
+ if (sw.softwareDescription().equals("OF-DPA 2.0")) {
+ flowRuleSet = createOfdpaFlowRules(serviceId, ceVlanId, vlanId, ingress, egress, first, last);
+ //createFilteringForwarding(serviceId, ceVlanId, vlanId, ingress, egress, first, last);
+ } else if (sw.factory().getVersion() == OFVersion.OF_13) {
+ flowRuleSet = createOF13FlowRule(serviceId, ceVlanId, vlanId, ingress, egress, first, last);
+ } else {
+ flowRuleSet = createOF10FlowRule(serviceId, ceVlanId, vlanId, ingress, egress, first, last);
+ }
+
+ return flowRuleSet;
+ }
+
+ /**
+ * Directly creates FlowRules according to the OFDPA pipeline.
+ * To be used instead of FlowObjectives until the OFDPA2Pipeline is modified appropriately
+ *
+ * @param serviceId User-provided identifier of the CE service
+ * @param vlanId VLAN id of the service
+ * @param ingress ingress connect point at the particular device
+ * @param first indicates whether the current device is the first one in the path
+ * @param last indicates whether the current device is the last one in the path
+ */
+ private Set<FlowRule> createOfdpaFlowRules(String serviceId, VlanId ceVlanId, VlanId vlanId,
+ ConnectPoint ingress, ConnectPoint egress, boolean first, boolean last) {
+
+ Set<FlowRule> flowRuleSet = new HashSet<>();
+
+ DeviceId deviceId = egress.deviceId();
+
+ // VLAN Table
+ // "Vlan Assignment"
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+ .matchInPort(ingress.port());
+
+ TrafficSelector.Builder preSelector = null;
+ TrafficTreatment.Builder preTreatment = null;
+
+ // Transition to TMAC table
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+ .transition(TMAC_TABLE);
+
+ if (first) {
+ // If this is a virtual service, match also on CE-VLAN ID at first hop
+ if (ceVlanId != null) {
+ OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(ceVlanId);
+ sBuilder.extension(ofdpaMatchVlanVid, deviceId);
+ OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanId);
+ tBuilder.extension(ofdpaSetVlanVid, deviceId);
+ } else {
+ OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.vlanId((short) 0));
+ sBuilder.extension(ofdpaMatchVlanVid, deviceId);
+ OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanId);
+ tBuilder.extension(ofdpaSetVlanVid, deviceId);
+ // XXX ofdpa will require an additional vlan match on the assigned vlan
+ // and it may not require the push. This is not in compliance with OF
+ // standard. Waiting on what the exact flows are going to look like.
+ preSelector = DefaultTrafficSelector.builder();
+ preSelector.matchInPort(ingress.port());
+ OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(vlanId);
+ preSelector.extension(preOfdpaMatchVlanVid, deviceId);
+ preTreatment = DefaultTrafficTreatment.builder().transition(20);
+ }
+ } else {
+ OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vlanId);
+ sBuilder.extension(ofdpaMatchVlanVid, deviceId);
+ }
+
+ FlowRule flowRule = createFlowRule(deviceId, PRIORITY, sBuilder.build(), tBuilder.build(), VLAN_TABLE);
+ flowRuleService.applyFlowRules(flowRule);
+ flowRuleSet.add(flowRule);
+
+ if (preSelector != null) {
+
+ flowRule = createFlowRule(deviceId, PRIORITY, preSelector.build(), preTreatment.build(), VLAN_TABLE);
+ flowRuleService.applyFlowRules(flowRule);
+ flowRuleSet.add(flowRule);
+ }
+
+ // TMAC Table defaults to Bridging Table
+
+ // Build group
+ GroupId groupId = createGroup(serviceId, vlanId, egress, first, last);
+
+ // ACL Table
+ // "IPv4 VLAN"
+
+ // NOTE: Directly adding vlanId match to builder causes rule to get continuously installed/uninstalled by ofdpa
+ sBuilder = DefaultTrafficSelector.builder()
+ .matchInPort(ingress.port())
+ .matchEthType(Ethernet.TYPE_IPV4);
+ //sBuilder.extension(new OfdpaMatchVlanVid(vlanId), deviceId);
+
+ // TODO: Check if there is existing FlowRule with same filtering and if yes modify this rule with an extra group
+ // TODO: NOTE: In OFDPA this probably will be done by first removing the existing flow
+
+ tBuilder = DefaultTrafficTreatment.builder().group(groupId);
+
+ flowRule = createFlowRule(deviceId, PRIORITY, sBuilder.build(), tBuilder.build(), ACL_TABLE);
+ flowRuleService.applyFlowRules(flowRule);
+ flowRuleSet.add(flowRule);
+
+ return flowRuleSet;
+ }
+
+ // Directly creates FlowRules using GROUP action (meant for OF1.3 non-OFDPA devices)
+ private Set<FlowRule> createOF13FlowRule(String serviceId, VlanId ceVlanId, VlanId vlanId,
+ ConnectPoint ingress, ConnectPoint egress,
+ boolean first, boolean last) {
+
+ Set<FlowRule> flowRuleSet = new HashSet<>();
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+ .matchInPort(ingress.port());
+
+ TrafficTreatment.Builder tBuilder;
+ if (first) {
+ // If this is a virtual service, match also on CE-VLAN ID at first hop
+ if (ceVlanId != null) {
+ sBuilder.matchVlanId(ceVlanId);
+ }
+ tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.pushVlan().setVlanId(vlanId);
+ } else {
+ sBuilder.matchVlanId(vlanId);
+ tBuilder = DefaultTrafficTreatment.builder();
+ }
+
+ // Build group
+ GroupId groupId = createGroup(serviceId, vlanId, egress, first, last);
+ tBuilder.group(groupId);
+
+ // Check if flow with same selector already exists. If yes, modify existing flow rule if needed
+ flowRuleService.getFlowRulesById(appId).forEach(flowRule -> {
+ if (flowRule.deviceId().equals(egress.deviceId()) && flowRule.selector().equals(sBuilder.build())) {
+ flowRule.treatment().allInstructions().forEach(instruction -> {
+ // If this is an GROUP instruction and group is different than existing, add the group
+ if (instruction.type() == Instruction.Type.GROUP &&
+ !(instruction.equals(Instructions.createGroup(groupId)))) {
+ tBuilder.add(instruction);
+ }
+ });
+ }
+ });
+
+ // FIXME: For efficiency do not send FlowMod again if the new treatment is exactly the same as the existing one
+ FlowRule flowRule = createFlowRule(egress.deviceId(), PRIORITY, sBuilder.build(), tBuilder.build(), 0);
+ flowRuleService.applyFlowRules(flowRule);
+ flowRuleSet.add(flowRule);
+
+ return flowRuleSet;
+ }
+
+ // Directly creates FlowRules using OUTPUT action (meant for OF1.0 non-OFDPA devices)
+ private Set<FlowRule> createOF10FlowRule(String serviceId, VlanId ceVlanId, VlanId vlanId,
+ ConnectPoint ingress, ConnectPoint egress,
+ boolean first, boolean last) {
+
+ Set<FlowRule> flowRuleSet = new HashSet<>();
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+ .matchInPort(ingress.port());
+
+ TrafficTreatment.Builder tBuilder;
+ if (first) {
+ // If this is a virtual service, match also on CE-VLAN ID at first hop
+ if (ceVlanId != null) {
+ sBuilder.matchVlanId(ceVlanId);
+ }
+ tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.pushVlan().setVlanId(vlanId);
+ } else {
+ sBuilder.matchVlanId(vlanId);
+ tBuilder = DefaultTrafficTreatment.builder();
+ }
+
+ if (last) {
+ tBuilder.popVlan();
+ }
+ tBuilder.setOutput(egress.port());
+
+ // Check if flow with same selector already exists. If yes, modify existing flow rule if needed
+ flowRuleService.getFlowRulesById(appId).forEach(flowRule -> {
+ if (flowRule.deviceId().equals(egress.deviceId()) && flowRule.selector().equals(sBuilder.build())) {
+ flowRule.treatment().allInstructions().forEach(instruction -> {
+ // If this is an OUTPUT instruction and output is different than existing, add the group
+ if (instruction.type() == Instruction.Type.OUTPUT &&
+ !(instruction.equals(Instructions.createOutput(egress.port())))) {
+ tBuilder.add(instruction);
+ }
+ });
+ }
+ });
+
+ // FIXME: For efficiency do not send FlowMod again if the new treatment is exactly the same as the existing one
+ FlowRule flowRule = createFlowRule(egress.deviceId(), PRIORITY, sBuilder.build(), tBuilder.build(), 0);
+ flowRuleService.applyFlowRules(flowRule);
+ flowRuleSet.add(flowRule);
+
+ return flowRuleSet;
+ }
+
+ /**
+ * Creates and submits FilteringObjective and ForwardingObjective with INDIRECT groups based on the role of the.
+ * specific device within the path.
+ *
+ * @param serviceId User-provided identifier of the CE service
+ * @param ceVlanId CE-VLAN id of the service, if present
+ * @param vlanId VLAN id of the service
+ * @param ingress ingress connect point at the particular device
+ * @param first indicates whether the current device is the first one in the path
+ * @param last indicates whether the current device is the last one in the path
+ */
+ private void createFilteringForwarding(String serviceId, VlanId ceVlanId, VlanId vlanId,
+ ConnectPoint ingress, ConnectPoint egress,
+ boolean first, boolean last) {
+
+ createFilteringObjective(ceVlanId, vlanId, ingress, first);
+ createForwardingObjective(serviceId, vlanId, ingress, egress, first, last);
+ }
+
+ /**
+ * Creates and submits FilteringObjective based on the role of the specific device within the path.
+ *
+ * @param ceVlanId the CE-VLAN id of the service, if present
+ * @param vlanId VLAN id of the service
+ * @param ingress ingress connect point at the particular device
+ * @param first indicates whether the current device is the first one in the path
+ */
+ private void createFilteringObjective(VlanId ceVlanId, VlanId vlanId, ConnectPoint ingress, boolean first) {
+
+ FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
+ TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
+
+ fob.withKey(Criteria.matchInPort(ingress.port()));
+ if (first) {
+ // If this is a virtual service, match also on CE-VLAN ID at first hop
+ if (ceVlanId != null) {
+ fob.addCondition(Criteria.matchVlanId(ceVlanId));
+ } else {
+ fob.addCondition(Criteria.matchVlanId(VlanId.NONE));
+ }
+ ttb.pushVlan().setVlanId(vlanId);
+ } else {
+ fob.addCondition(Criteria.matchVlanId(vlanId));
+ }
+
+ fob.withPriority(PRIORITY);
+ fob.withMeta(ttb.build());
+ fob.permit().fromApp(appId);
+
+ flowObjectiveService.filter(ingress.deviceId(), fob.add());
+
+ }
+
+ /**
+ * Creates and submits ForwardingObjective based on the role of the specific device within the path.
+ *
+ * @param serviceId the CE service id
+ * @param vlanId VLAN id of the service
+ * @param ingress ingress connect point at the particular device
+ * @param egress egress connect point at the particular device
+ * @param first indicates whether the current device is the first one in the path
+ * @param last indicates whether the current device is the last one in the path
+ */
+ private void createForwardingObjective(String serviceId, VlanId vlanId, ConnectPoint ingress, ConnectPoint egress,
+ boolean first, boolean last) {
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchVlanId(vlanId)
+ .matchInPort(ingress.port())
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .build();
+
+ GroupId groupId = createGroup(serviceId, vlanId, egress, first, last);
+
+ // Add group to original treatment
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().group(groupId).build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(appId)
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(PRIORITY)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ flowObjectiveService.forward(ingress.deviceId(), forwardingObjective);
+ }
+
+ /**
+ * Creates INDIRECT group with single bucket and submits it to GroupService.
+ *
+ * @param serviceId User-provided identifier of the CE service
+ * @param vlanId VLAN id of the service
+ * @param egress egress connect point at the particular device
+ * @param first indicates whether the current device is the first one in the path
+ * @param last indicates whether the current device is the last one in the path
+ * @return The GroupId of the created Group
+ */
+ private GroupId createGroup(String serviceId, VlanId vlanId, ConnectPoint egress, boolean first, boolean last) {
+
+ checkNotNull(serviceId);
+
+ DeviceId deviceId = egress.deviceId();
+
+ GroupKey groupKey = getGroupKey(vlanId, egress);
+ Group group = groupService.getGroup(deviceId, groupKey);
+ GroupId groupId = getGroupId(vlanId, egress);
+
+ if (group != null) {
+ log.warn("Group {} already exists in {}", groupKey.toString(), deviceId);
+ return groupId;
+ }
+
+ GroupBuckets buckets = getGroupBuckets(egress, last);
+
+ GroupDescription groupDescription = new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.INDIRECT,
+ buckets,
+ groupKey,
+ groupId.id(),
+ appId);
+
+ groupService.addGroup(groupDescription);
+
+ return groupId;
+ }
+
+ /**
+ * Currently creates single group bucket to be used instead of an OUTPUT action.
+ *
+ * @param egress egress connect point at the particular device
+ * @param last indicates whether the current device is the last one in the path
+ * @return GroupBuckets which can be used to create a GroupDescription
+ */
+ private GroupBuckets getGroupBuckets(ConnectPoint egress, boolean last) {
+
+ List<GroupBucket> buckets = Lists.newArrayList();
+
+ TrafficTreatment.Builder treatmentBuilder;
+ if (last) {
+ treatmentBuilder = DefaultTrafficTreatment.builder();
+ treatmentBuilder.popVlan();
+ } else {
+ treatmentBuilder = DefaultTrafficTreatment.builder();
+ }
+ TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
+
+ buckets.add(DefaultGroupBucket.createIndirectGroupBucket(treatment));
+
+ return new GroupBuckets(buckets);
+ }
+
+ /**
+ * Returns globally unique group id according to OFDPA 2.0 specification for "L2 Interface" group types.
+ *
+ * @param vlanId VLAN id of the service
+ * @param egress egress connect point at the particular device
+ * @return group id
+ */
+ private GroupId getGroupId(VlanId vlanId, ConnectPoint egress) {
+ return new DefaultGroupId((vlanId.toShort()) << 16 | Integer.parseInt(egress.port().toString()));
+ }
+
+ /**
+ * Returns globally unique group key.
+ *
+ * @param vlanId VLAN id of the service
+ * @param egress egress connect point at the particular device
+ * @return group key
+ */
+ private GroupKey getGroupKey(VlanId vlanId, ConnectPoint egress) {
+ //TODO: Create GroupKey in a better way - perhaps the same as GroupId (unique per device)
+ return new DefaultGroupKey(Integer.toString(Objects.hash(egress.deviceId(), egress.port(), vlanId)).getBytes());
+ }
+
+ @Override
+ void applyBandwidthProfileResources(String serviceId, CarrierEthernetUni uni) {
+
+ Dpid dpid = Dpid.dpid(uni.cp().deviceId().uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+
+ // FIXME: Temporary hack: Do not apply meters to OFDPA2.0 switches
+ if (sw.softwareDescription().equals("OF-DPA 2.0")) {
+ return;
+ }
+
+ // Create meters and add them to global MeterId map
+ Set<DeviceMeterId> deviceMeterIdSet = deviceMeterIdMap.get(serviceId);
+ if (deviceMeterIdSet == null) {
+ deviceMeterIdSet = new HashSet<>();
+ }
+ deviceMeterIdSet.addAll(createMeters(uni));
+ deviceMeterIdMap.put(serviceId, deviceMeterIdSet);
+
+ // Apply meters to already installed flows
+
+ Set<FlowRule> newFlowRuleSet = new HashSet<>();
+
+ // Get flow rules belonging to service and having as in_port the UNI connect point
+ flowRuleMap.get(serviceId).forEach(flowRule -> {
+ PortNumber inPort = ((PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT)).port();
+ ConnectPoint flowInCp = new ConnectPoint(flowRule.deviceId(), inPort);
+ //VlanId flowInVlanId = ((VlanIdCriterion) flowRule.selector().
+ // getCriterion(Criterion.Type.VLAN_VID)).vlanId();
+ // TODO: Compare also to the CE-VLAN ID (if it is not null)
+ // FIXME: Maybe check only in_port, vlanid, and if there is output port or group action?
+ if (uni.cp().equals(flowInCp)) {
+ //if (uni.cp().equals(flowInCp) && (uni.ceVlanId() == null || uni.ceVlanId().equals(flowInVlanId))) {
+ // Need to add to the flow the meters associated with the same device
+ Set<DeviceMeterId> tmpDeviceMeterIdSet = new HashSet<>();
+ deviceMeterIdMap.get(serviceId).forEach(deviceMeterId -> {
+ if (deviceMeterId.deviceId().equals(flowRule.deviceId())) {
+ tmpDeviceMeterIdSet.add(deviceMeterId);
+ }
+ });
+ // Modify and submit flow rule only if there are meters to add
+ if (!tmpDeviceMeterIdSet.isEmpty()) {
+ FlowRule newFlowRule = addMetersToFlowRule(flowRule, tmpDeviceMeterIdSet);
+ flowRuleService.applyFlowRules(newFlowRule);
+ newFlowRuleSet.add(newFlowRule);
+ } else {
+ newFlowRuleSet.add(flowRule);
+ }
+ } else {
+ newFlowRuleSet.add(flowRule);
+ }
+ });
+
+ flowRuleMap.put(serviceId, newFlowRuleSet);
+ }
+
+ /**
+ * Creates and submits a meter with the required bands for a UNI.
+ *
+ * @param uni the UNI descriptor
+ * @return set of meter ids of the meters created
+ */
+ private Set<DeviceMeterId> createMeters(CarrierEthernetUni uni) {
+
+ // TODO: Check if meter already exists before adding it?
+
+ Set<DeviceMeterId> deviceMeterIdSet = new HashSet<>();
+
+ long longCir = (long) (uni.bwp().cir().bps() / 8000);
+ long longEir = (long) (uni.bwp().eir().bps() / 8000);
+
+ MeterRequest.Builder meterRequestBuilder;
+ Meter meter;
+ Band.Builder bandBuilder;
+
+ Set<Band> bandSet = new HashSet<>();
+
+ // If EIR is zero do not create the REMARK meter
+ if (longEir != 0) {
+ // Mark frames that exceed CIR as Best Effort
+ bandBuilder = DefaultBand.builder()
+ .ofType(Band.Type.REMARK)
+ .withRate(longCir)
+ .dropPrecedence((short) 0);
+
+ if (uni.bwp().cbs() != 0) {
+ bandBuilder.burstSize(uni.bwp().cbs());
+ }
+
+ bandSet.add(bandBuilder.build());
+ }
+
+ // If CIR is zero do not create the DROP meter
+ if (longCir != 0) {
+ // Drop all frames that exceed CIR + EIR
+ bandBuilder = DefaultBand.builder()
+ .ofType(Band.Type.DROP)
+ .withRate(longCir + longEir);
+
+ if (uni.bwp().cbs() != 0 || uni.bwp().ebs() != 0) {
+ // FIXME: Use CBS and EBS correctly according to MEF specs
+ bandBuilder.burstSize(uni.bwp().cbs() + uni.bwp().ebs());
+ }
+
+ bandSet.add(bandBuilder.build());
+ }
+
+ // Create meter only if at least one band was created
+ if (!bandSet.isEmpty()) {
+ meterRequestBuilder = DefaultMeterRequest.builder()
+ .forDevice(uni.cp().deviceId())
+ .fromApp(appId)
+ .withUnit(Meter.Unit.KB_PER_SEC)
+ .withBands(bandSet);
+
+ if (uni.bwp().cbs() != 0 || uni.bwp().ebs() != 0) {
+ meterRequestBuilder.burst();
+ }
+
+ meter = meterService.submit(meterRequestBuilder.add());
+ deviceMeterIdSet.add(new DeviceMeterId(uni.cp().deviceId(), meter.id()));
+ }
+
+ return deviceMeterIdSet;
+ }
+
+ private FlowRule addMeterToFlowRule(FlowRule flowRule, DeviceMeterId deviceMeterId) {
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+ .builder(flowRule.treatment());
+
+ tBuilder.add(Instructions.meterTraffic(deviceMeterId.meterId()));
+
+ return createFlowRule(flowRule.deviceId(), flowRule.priority(),
+ flowRule.selector(), tBuilder.build(), flowRule.tableId());
+ }
+
+ private FlowRule addMetersToFlowRule(FlowRule flowRule, Set<DeviceMeterId> deviceMeterIdSet) {
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+ .builder(flowRule.treatment());
+
+ deviceMeterIdSet.forEach(deviceMeterId -> {
+ //tBuilder.add(Instructions.meterTraffic(deviceMeterId.meterId()));
+ tBuilder.meter(deviceMeterId.meterId());
+ });
+
+ return createFlowRule(flowRule.deviceId(), flowRule.priority(),
+ flowRule.selector(), tBuilder.build(), flowRule.tableId());
+ }
+
+ @Override
+ void removeBandwidthProfileResources(String serviceId, CarrierEthernetUni uni) {
+
+ removeMeters(serviceId, uni);
+ }
+
+ /**
+ * Removes the meters associated with a specific UNI of a service.
+ *
+ * @param serviceId the CE service ID
+ * @param uni the UNI descriptor
+ * */
+ private void removeMeters(String serviceId, CarrierEthernetUni uni) {
+
+ Set<DeviceMeterId> newDeviceMeterIdSet = deviceMeterIdMap.get(serviceId);
+ DeviceMeterId tmpDeviceMeterId;
+
+ Collection<Meter> meters = meterService.getMeters(uni.cp().deviceId());
+
+ Iterator<Meter> it = meters.iterator();
+ while (it.hasNext()) {
+ Meter meter = it.next();
+ tmpDeviceMeterId = new DeviceMeterId(uni.cp().deviceId(), meter.id());
+ if (meter.appId().equals(appId) &&
+ deviceMeterIdMap.get(serviceId).contains(tmpDeviceMeterId)) {
+ MeterRequest.Builder mBuilder;
+ mBuilder = DefaultMeterRequest.builder()
+ .fromApp(meter.appId())
+ .forDevice(meter.deviceId())
+ .withUnit(meter.unit())
+ .withBands(meter.bands());
+ if (uni.bwp().cbs() != 0 || uni.bwp().ebs() != 0) {
+ mBuilder.burst();
+ }
+ meterService.withdraw(mBuilder.remove(), meter.id());
+ newDeviceMeterIdSet.remove(tmpDeviceMeterId);
+ }
+ }
+
+ deviceMeterIdMap.put(serviceId, newDeviceMeterIdSet);
+ }
+
+ @Override
+ void removeAllForwardingResources(CarrierEthernetService service) {
+ removeFlowRules(service.id());
+ removeGroups(service);
+ }
+
+ /**
+ * Removes all flow rules installed by the application which are associated with a specific CE service.
+ *
+ * @param serviceId the CE service id
+ * */
+ private void removeFlowRules(String serviceId) {
+ // Note: A Flow Rule cannot be shared by multiple services due to different VLAN or CE-VLAN ID match.
+ Set<FlowRule> flowRuleSet = flowRuleMap.remove(serviceId);
+ flowRuleSet.forEach(flowRule -> flowRuleService.removeFlowRules(flowRule));
+ }
+
+ /**
+ * Removes all groups installed by the application which are associated with a specific CE service.
+ *
+ * @param service the CE service definition
+ * */
+ // Note: A Group cannot be shared by multiple services since GroupIds/GroupKeys include the service VLAN ID
+ private void removeGroups(CarrierEthernetService service) {
+
+ Set<ConnectPoint> egressCpSet = egressCpMap.remove(service.id());
+ Set<ConnectPoint> uniCpSet = new HashSet<>();
+
+ service.uniSet().forEach(uni -> uniCpSet.add(uni.cp()));
+
+ egressCpSet.forEach(egress -> {
+ // The connect points associated with UNIs are the ones including the VLAN pop commands, i.e. the "last"
+ boolean last = (uniCpSet.contains(egress));
+ DeviceId deviceId = egress.deviceId();
+ GroupKey groupKey = getGroupKey(service.vlanId(), egress);
+ GroupBuckets buckets = getGroupBuckets(egress, last);
+ if (groupService.getGroup(deviceId, groupKey) != null) {
+ // Note: Removing buckets before removing group in CpQD causes warnings (but is needed in OFDPA2.0)
+ Dpid dpid = Dpid.dpid(deviceId.uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ if (sw.softwareDescription().equals("OF-DPA 2.0")) {
+ groupService.removeBucketsFromGroup(
+ deviceId,
+ groupKey,
+ buckets,
+ groupKey,
+ appId);
+ }
+ log.info("Trying to remove group with key {} from {}", groupKey, deviceId);
+ groupService.removeGroup(deviceId, groupKey, appId);
+ }
+ });
+ }
+
+ /**
+ * Utility class to compensate for the fact that MeterIds are not unique system-wide.
+ * */
+ class DeviceMeterId {
+ private DeviceId deviceId;
+ private MeterId meterId;
+
+ DeviceMeterId(DeviceId deviceId, MeterId meterId) {
+ this.deviceId = deviceId;
+ this.meterId = meterId;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public MeterId meterId() {
+ return meterId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(deviceId, meterId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DeviceMeterId) {
+ DeviceMeterId other = (DeviceMeterId) obj;
+ if (this.deviceId().equals(other.deviceId()) && this.meterId().equals(other.meterId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private FlowRule createFlowRule(DeviceId deviceId, int priority,
+ TrafficSelector selector, TrafficTreatment treatment, int tableId) {
+ return DefaultFlowRule.builder()
+ .fromApp(appId)
+ .forDevice(deviceId)
+ .makePermanent()
+ .withPriority(priority)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .forTable(tableId)
+ .build();
+ }
+
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketNodeManager.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketNodeManager.java
new file mode 100644
index 0000000..3b2b771
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketNodeManager.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.app;
+
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Abstraction of a class used to control Carrier Ethernet nodes according to their control protocol.
+ */
+public abstract class CarrierEthernetPacketNodeManager {
+
+ abstract void setNodeForwarding(CarrierEthernetService service, CarrierEthernetUni srcUni,
+ CarrierEthernetUni dstUni, ConnectPoint ingress, ConnectPoint egress,
+ boolean first, boolean last);
+
+ abstract void applyBandwidthProfileResources(String serviceId, CarrierEthernetUni uni);
+
+ abstract void removeBandwidthProfileResources(String serviceId, CarrierEthernetUni uni);
+
+ abstract void removeAllForwardingResources(CarrierEthernetService service);
+
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketProvisioner.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketProvisioner.java
new file mode 100644
index 0000000..a2d993a
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetPacketProvisioner.java
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.app;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.Device;
+import org.onosproject.net.Link;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Path;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.constraint.BandwidthConstraint;
+import org.onosproject.net.intent.constraint.LatencyConstraint;
+import org.onosproject.net.topology.PathService;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
+
+import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Component(immediate = true)
+@Service (value = CarrierEthernetPacketProvisioner.class)
+public class CarrierEthernetPacketProvisioner {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CarrierEthernetOpenFlowPacketNodeManager ceOfPktNodeManager;
+
+ @Activate
+ protected void activate() {}
+
+ @Deactivate
+ protected void deactivate() {
+
+ }
+
+ public boolean setupConnectivity(CarrierEthernetUni uni1, CarrierEthernetUni uni2, CarrierEthernetService service) {
+
+ // Find the paths for both directions at the same time, so that we can skip the pair if needed
+ List<Link> forwardLinks = selectLinkPath(uni1, uni2, service);
+ List<Link> backwardLinks = selectLinkPath(uni2, uni1, service);
+
+ // Skip this UNI pair if no feasible path could be found
+ if (forwardLinks == null || (!service.congruentPaths() && backwardLinks == null)) {
+ log.warn("There are no feasible paths between {} and {}.",
+ uni1.cp().deviceId(), uni2.cp().deviceId());
+ return false;
+ }
+
+ // Establish connectivity for the packet switches
+ // TODO: Send some kind of gRPC message to BigSwitches
+ for (int i = 0; i < forwardLinks.size() - 1; i++) {
+ // Create flows for the forward direction
+ boolean first = isFirst(i);
+ boolean last = isLast(forwardLinks, i);
+ ConnectPoint ingress = forwardLinks.get(i).dst();
+ ConnectPoint egress = forwardLinks.get(i + 1).src();
+ // TODO: Select node manager depending on device protocol
+ // Set forwarding only on packet switches
+ if (deviceService.getDevice(ingress.deviceId()).type().equals(Device.Type.SWITCH)) {
+ ceOfPktNodeManager.setNodeForwarding(service, uni1, uni2, ingress, egress, first, last);
+ }
+
+ if (service.congruentPaths()) {
+ // Create flows for the forward direction using the reverse path
+ ingress = forwardLinks.get(forwardLinks.size() - i - 1).src();
+ egress = forwardLinks.get(forwardLinks.size() - i - 2).dst();
+ // TODO: Select node manager depending on device protocol
+ if (deviceService.getDevice(ingress.deviceId()).type().equals(Device.Type.SWITCH)) {
+ ceOfPktNodeManager.setNodeForwarding(service, uni2, uni1, ingress, egress, first, last);
+ }
+ }
+ }
+
+ if (!service.congruentPaths()) {
+ // Create flows for the backward direction using a path potentially different from the reverse one
+ for (int i = 0; i < backwardLinks.size() - 1; i++) {
+ boolean first = isFirst(i);
+ boolean last = isLast(backwardLinks, i);
+ ConnectPoint ingress = backwardLinks.get(i).dst();
+ ConnectPoint egress = backwardLinks.get(i + 1).src();
+ // TODO: Select node manager depending on device protocol
+ if (deviceService.getDevice(ingress.deviceId()).type().equals(Device.Type.SWITCH)) {
+ ceOfPktNodeManager.setNodeForwarding(service, uni2, uni1, ingress, egress, first, last);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Select a feasible link path between two UNIs based on the CE service parameters.
+ *
+ * @param uni1 the first UNI
+ * @param uni2 the second UNI
+ * @param service the CE service descriptor
+ */
+ private List<Link> selectLinkPath(CarrierEthernetUni uni1, CarrierEthernetUni uni2,
+ CarrierEthernetService service) {
+
+ List<Constraint> constraints = ImmutableList.<Constraint>builder()
+ .add(new BandwidthConstraint(uni1.bwp().cir()))
+ .add(new LatencyConstraint(service.latency()))
+ .build();
+
+ Set<Path> paths = pathService.getPaths(uni1.cp().deviceId(), uni2.cp().deviceId());
+
+ Path path = null;
+
+ for (Path p : paths) {
+ // TODO: Select path in more sophisticated way and return null if any of the constraints cannot be met
+ path = p;
+ break;
+ }
+
+ if (path == null) {
+ return null;
+ } else {
+ List<Link> links = new ArrayList<>();
+ links.add(createEdgeLink(uni1.cp(), true));
+ links.addAll(path.links());
+ links.add(createEdgeLink(uni2.cp(), false));
+ return links;
+ }
+ }
+
+ private boolean isLast(List<Link> links, int i) {
+ return i == links.size() - 2;
+ }
+
+ private boolean isFirst(int i) {
+ return i == 0;
+ }
+
+ public void removeConnectivity(CarrierEthernetService service) {
+ // TODO: Add here the same call for all node manager types
+ ceOfPktNodeManager.removeAllForwardingResources(service);
+ }
+
+ /**
+ * Applies bandwidth profiles to the UNIs of a service.
+ *
+ * @param service the CE service definition
+ */
+ public void applyBandwidthProfiles(CarrierEthernetService service) {
+ // TODO: Select node manager depending on device protocol
+ service.uniSet().forEach(uni -> ceOfPktNodeManager.applyBandwidthProfileResources(service.id(), uni));
+ }
+
+ /**
+ * Removes bandwidth profiles from the UNIs of a service.
+ *
+ * @param service the CE service definition
+ */
+ public void removeBandwidthProfiles(CarrierEthernetService service) {
+ // TODO: Select node manager depending on device protocol
+ service.uniSet().forEach(uni -> ceOfPktNodeManager.removeBandwidthProfileResources(service.id(), uni));
+ }
+
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetService.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetService.java
new file mode 100644
index 0000000..f688768
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetService.java
@@ -0,0 +1,261 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.app;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.ecord.metro.api.MetroConnectivityId;
+import org.onosproject.ecord.metro.api.MetroPathEvent;
+
+import java.time.Duration;
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Representation of a Carrier Ethernet Service along with relevant ONOS-related resources.
+ */
+public class CarrierEthernetService {
+
+ public enum Type {
+ POINT_TO_POINT, MULTIPOINT_TO_MULTIPOINT, ROOT_MULTIPOINT
+ }
+
+ protected String serviceId;
+ protected String serviceCfgId;
+ protected Type serviceType;
+ protected VlanId vlanId;
+ protected boolean isVirtual;
+ protected Set<CarrierEthernetUni> uniSet;
+ protected Duration latency;
+ protected CarrierEthernetServiceMetroConnectivity metroConnectivity;
+ protected boolean congruentPaths;
+
+ // Set to true if both directions should use the same path
+ private static final boolean CONGRUENT_PATHS = true;
+
+ private static final Duration DEFAULT_LATENCY = Duration.ofMillis(50);
+
+ // Note: serviceId should be provided only when updating an existing service
+ public CarrierEthernetService(String serviceId, String serviceCfgId, Type serviceType,
+ Set<CarrierEthernetUni> uniSet) {
+ this.serviceId = serviceId;
+ this.serviceCfgId = serviceCfgId;
+ this.serviceType = serviceType;
+ this.vlanId = null;
+ this.uniSet = new HashSet<>();
+ this.uniSet.addAll(uniSet);
+ this.congruentPaths = CONGRUENT_PATHS;
+ this.latency = DEFAULT_LATENCY;
+ this.metroConnectivity = new CarrierEthernetServiceMetroConnectivity(null, MetroPathEvent.Type.PATH_REMOVED);
+ }
+
+ /**
+ * Returns service identifier.
+ *
+ * @return service identifier
+ */
+ public String id() {
+ return serviceId;
+ }
+
+ /**
+ * Returns service config identifier.
+ *
+ * @return service config identifier
+ */
+ public String cfgId() {
+ return serviceCfgId;
+ }
+
+ /**
+ * Returns type of service.
+ *
+ * @return type of service
+ */
+ public Type type() {
+ return serviceType;
+ }
+
+ /**
+ * Returns Vlan id.
+ *
+ * @return Vlan id
+ */
+ public VlanId vlanId() {
+ return vlanId;
+ }
+
+ /**
+ * Returns the Virtual status of the service (i.e. if all UNIs have CE-VLAN ids).
+ *
+ * @return true if service is virtual, false otherwise
+ */
+ public boolean isVirtual() {
+ return isVirtual;
+ }
+
+ /**
+ * Returns set of UNIs.
+ *
+ * @return set of UNIs
+ */
+ public Set<CarrierEthernetUni> uniSet() {
+ return uniSet;
+ }
+
+ /**
+ * Returns latency constraint.
+ *
+ * @return latency constraint
+ */
+ public Duration latency() {
+ return latency;
+ }
+
+ /**
+ * Returns true if service requires congruent paths.
+ *
+ * @return true if congruent paths required
+ */
+ public boolean congruentPaths() {
+ return congruentPaths;
+ }
+
+ /**
+ * Sets service identifier.
+ *
+ * @param serviceId the service identifier to set
+ */
+ public void setId(String serviceId) {
+ this.serviceId = serviceId;
+ }
+
+ /**
+ * Sets service config identifier.
+ *
+ * @param serviceCfgId service config identifier
+ */
+ public void setCfgId(String serviceCfgId) {
+ this.serviceCfgId = serviceCfgId;
+ }
+
+ /**
+ * Sets the set of UNIs.
+ *
+ * @param uniSet the set of UNIs to be set
+ */
+ public void setUniSet(Set<CarrierEthernetUni> uniSet) {
+ this.uniSet = uniSet;
+ }
+
+ /**
+ * Sets the value of the congruent paths parameter.
+ *
+ * @param congruentPaths the congruent paths parameter value to set
+ */
+ public void setCongruentPaths(boolean congruentPaths) {
+ this.congruentPaths = congruentPaths;
+ }
+
+ /**
+ * Sets the vlanId to be used by the service.
+ *
+ * @param vlanId the vlanId to set
+ */
+ public void setVlanId(VlanId vlanId) {
+ this.vlanId = vlanId;
+ }
+
+ /**
+ * Sets the Virtual status of the service.
+ *
+ * @param isVirtual boolean value with the status to set
+ */
+ public void setIsVirtual(boolean isVirtual) {
+ this.isVirtual = isVirtual;
+ }
+
+ /**
+ * Gets metro connectivity id.
+ *
+ * @return the metro connectivity of the service
+ */
+ public CarrierEthernetServiceMetroConnectivity metroConnectivity() {
+ return this.metroConnectivity;
+ }
+
+ /**
+ * Sets metro connectivity id.
+ *
+ * @param id the metro connectivity identifier to set
+ */
+ public void setMetroConnectivityId(MetroConnectivityId id) {
+ this.metroConnectivity.setId(id);
+ }
+
+ /**
+ * Sets metro connectivity status.
+ *
+ * @param status the metro connectivity status
+ */
+ public void setMetroConnectivityStatus(MetroPathEvent.Type status) {
+ this.metroConnectivity.setStatus(status);
+ }
+
+ public String toString() {
+
+ return toStringHelper(this)
+ .add("id", serviceId)
+ .add("cfgId", serviceCfgId)
+ .add("type", serviceType)
+ .add("vlanId", vlanId)
+ .add("metroConnectId", (metroConnectivity.id() == null ? "null" : metroConnectivity.id().value()))
+ .add("UNIs", uniSet).toString();
+ }
+
+ class CarrierEthernetServiceMetroConnectivity {
+
+ // TODO: In the future this may be replaced by a connectivity intent
+ // FIXME: Need to keep a set of MetroConnectivityIds
+
+ private MetroConnectivityId id;
+ private MetroPathEvent.Type status;
+
+ CarrierEthernetServiceMetroConnectivity(MetroConnectivityId id, MetroPathEvent.Type status) {
+ this.id = id;
+ this.status = status;
+ }
+
+ public MetroConnectivityId id() {
+ return this.id;
+ }
+
+ public MetroPathEvent.Type status() {
+ return this.status;
+ }
+
+ public void setId(MetroConnectivityId id) {
+ this.id = id;
+ }
+
+ public void setStatus(MetroPathEvent.Type status) {
+ this.status = status;
+ }
+
+ }
+
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetUni.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetUni.java
new file mode 100644
index 0000000..d68bd33
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetUni.java
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.app;
+
+import org.onlab.packet.VlanId;
+import org.onlab.util.Bandwidth;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.device.DeviceService;
+import org.slf4j.Logger;
+
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Representation of a Carrier Ethernet UNI.
+ * Class can be used in different two ways:
+ * 1. As a global UNI descriptor containing one or more BW profiles
+ * 2. As a service-specific UNI descriptor containing a single BW profile and including a type (root, leaf)
+ */
+public class CarrierEthernetUni {
+
+ private final Logger log = getLogger(getClass());
+
+ protected DeviceService deviceService = AbstractShellCommand.get(DeviceService.class);
+
+ public enum Type {
+ ROOT, LEAF
+ }
+
+ protected ConnectPoint connectPoint;
+ protected String uniId;
+ protected String uniCfgId;
+ protected Type type;
+ protected Set<VlanId> ceVlanIdSet;
+ protected Bandwidth capacity;
+ protected Bandwidth usedCapacity;
+
+ // Note: INTERFACE BWP map can only have up to one element
+ protected final Map<CarrierEthernetBandwidthProfile.Type, Map<String, CarrierEthernetBandwidthProfile>> bwpMap =
+ new HashMap<>();
+
+ // TODO: May be needed to add refCount for CoS BWPs - only applicable to global UNIs
+
+ public CarrierEthernetUni(ConnectPoint connectPoint, String uniCfgId, Type type, VlanId ceVlanId,
+ CarrierEthernetBandwidthProfile bwp) {
+ // TODO: Check for null
+ this.connectPoint = connectPoint;
+ this.uniId = this.connectPoint.deviceId().toString() + "/" + this.connectPoint.port().toString();
+ this.uniCfgId = (uniCfgId == null ? this.uniId : uniCfgId);
+ this.type = type;
+ this.ceVlanIdSet = new HashSet<>();
+ if (ceVlanId != null) {
+ this.ceVlanIdSet.add(ceVlanId);
+ }
+ this.capacity = Bandwidth.mbps(deviceService.getPort(connectPoint.deviceId(), connectPoint.port())
+ .portSpeed());
+ this.usedCapacity = Bandwidth.mbps((double) 0);
+ for (CarrierEthernetBandwidthProfile.Type bwpType : CarrierEthernetBandwidthProfile.Type.values()) {
+ this.bwpMap.put(bwpType, new HashMap<>());
+ }
+
+ if (bwp != null) {
+ // Limit the CIR of the provided bwp according to UNI capacity
+ if (bwp.cir().bps() > this.capacity.bps()) {
+ log.warn("UNI {}: Limiting provided CIR ({} bps) to UNI capacity ({} bps)",
+ this.uniId, (long) bwp.cir().bps(), this.capacity);
+ }
+ bwp.setCir(Bandwidth.bps(Math.min(bwp.cir().bps(), this.capacity.bps())));
+
+ // Limit the EIR of the provided bwp according to the UNI capacity minus CIR
+ if (bwp.eir().bps() > this.capacity.bps() - bwp.cir().bps()) {
+ log.warn("UNI {}: Limiting provided EIR ({} bps) to UNI capacity minus CIR ({} bps)",
+ this.uniId, bwp.eir().bps(), this.capacity.bps() - bwp.cir().bps());
+ }
+ bwp.setEir(Bandwidth.bps(Math.min(bwp.eir().bps(), this.capacity.bps() - bwp.cir().bps())));
+
+ addBandwidthProfile(bwp);
+ }
+ }
+
+ /**
+ * Adds the resources associated with a service-specific UNI to a global UNI.
+ *
+ * @param uni the service UNI to be added
+ */
+ public void addServiceUni(CarrierEthernetUni uni) {
+
+ // Add CE-VLAN ID
+ if (uni.ceVlanId() != null) {
+ this.ceVlanIdSet.add(uni.ceVlanId());
+ }
+
+ // Add UNI BWP
+ CarrierEthernetBandwidthProfile bwp = uni.bwp();
+ Map<String, CarrierEthernetBandwidthProfile> subBwpMap = this.bwpMap.get(bwp.type());
+ subBwpMap.put(bwp.id(), bwp);
+ this.bwpMap.put(bwp.type(), subBwpMap);
+ // Used capacity cannot be more than UNI capacity (redundant check - should be avoided by check in validateBwp)
+ this.usedCapacity = Bandwidth.bps(Math.min(this.usedCapacity.bps() + bwp.cir().bps(), this.capacity.bps()));
+ }
+
+ /**
+ * Adds a BW profile to a UNI.
+ *
+ * @param bwp the BWP to be added
+ */
+ public void addBandwidthProfile(CarrierEthernetBandwidthProfile bwp) {
+
+ Map<String, CarrierEthernetBandwidthProfile> subBwpMap = this.bwpMap.get(bwp.type());
+ subBwpMap.put(bwp.id(), bwp);
+ this.bwpMap.put(bwp.type(), subBwpMap);
+ // Used capacity cannot be more than UNI capacity (redundant check - should be avoided by check in validateBwp)
+ this.usedCapacity = Bandwidth.bps(Math.min(this.usedCapacity.bps() + bwp.cir().bps(), this.capacity.bps()));
+ }
+
+ /**
+ * Removes the resources associated with a service-specific UNI from a global UNI.
+ *
+ * @param uni the service UNI to be added
+ */
+ public void removeServiceUni(CarrierEthernetUni uni) {
+
+ // Remove UNI CE-VLAN ID
+ ceVlanIdSet.remove(uni.ceVlanId());
+
+ // Remove UNI BWP
+ CarrierEthernetBandwidthProfile bwp = uni.bwp();
+ Map<String, CarrierEthernetBandwidthProfile> subBwpMap = this.bwpMap.get(bwp.type());
+ subBwpMap.remove(bwp.id());
+ this.bwpMap.put(bwp.type(), subBwpMap);
+ // Redundant check - should be avoided by check in validateBwp
+ this.usedCapacity = Bandwidth.bps(Math.max(this.usedCapacity.bps() - bwp.cir().bps(), 0));
+ }
+
+ /**
+ * Validates whether a service-specific UNI is compatible with a global UNI.
+ *
+ * @param uni the service-specific UNI
+ * @return boolean value indicating whether the UNIs are compatible
+ */
+ public boolean validateServiceUni(CarrierEthernetUni uni) {
+
+ // Check if the CE-VLAN ID of the UNI is already included in global UNI
+ if (uni.ceVlanId() != null) {
+ if (ceVlanIdSet.contains(uni.ceVlanId())) {
+ log.error("CE-VLAN ID {} already exists in UNI {}", uni.ceVlanId().toString(), this.id());
+ return false;
+ }
+ }
+
+ CarrierEthernetBandwidthProfile bwp = uni.bwp();
+
+ // Check if the UNI BW profile is allowed based on its type and id and the existing profiles on the global UNI
+ for (CarrierEthernetBandwidthProfile.Type bwpType : CarrierEthernetBandwidthProfile.Type.values()) {
+ Map<String, CarrierEthernetBandwidthProfile> subBwpMap = this.bwpMap.get(bwpType);
+ if (!(subBwpMap.isEmpty())) {
+ if (bwpType != bwp.type()) {
+ log.error("Different bandwidth profile type than {} already exists in UNI {}",
+ bwp.type().name(), this.id());
+ return false;
+ } else if (subBwpMap.containsKey(bwp.id())) {
+ log.error("Bandwidth profile {} already exists in UNI {}", bwp.id(), this.id());
+ return false;
+ } else if (bwp.type().equals(CarrierEthernetBandwidthProfile.Type.INTERFACE)) {
+ log.error("Another bandwidth profile already exists in UNI {}", this.id());
+ return false;
+ }
+ }
+ }
+
+ // Check whether there are enough available resources on the UNI
+ if (usedCapacity.bps() + bwp.cir().bps() > capacity.bps()) {
+ log.error("Bandwidth profile {} cannot be added to UNI {} due to lack of resources", bwp.id(), this.id());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns associated connect point.
+ *
+ * @return associated connect point
+ */
+ public ConnectPoint cp() {
+ return connectPoint;
+ }
+
+ /**
+ * Returns UNI string identifier.
+ *
+ * @return UNI string identifier
+ */
+ public String id() {
+ return uniId;
+ }
+
+ /**
+ * Returns UNI string config identifier.
+ *
+ * @return UNI string config identifier
+ */
+ public String cfgId() {
+ return uniCfgId;
+ }
+
+ /**
+ * Returns UNI type (ROOT or LEAF) - applicable only to service-specific UNIs.
+ *
+ * @return UNI type
+ */
+ public Type type() {
+ return type;
+ }
+
+ /**
+ * Returns the CE-VLAN id associated with a local UNI, or the first CE-VLAN ID found for a global UNI.
+ *
+ * @return CE-VLAN id
+ */
+ public VlanId ceVlanId() {
+ if (ceVlanIdSet.isEmpty()) {
+ return null;
+ } else {
+ return ceVlanIdSet.iterator().next();
+ }
+ }
+
+ /**
+ * Returns the set of CE-VLAN ids associated with the UNI.
+ *
+ * @return CE-VLAN id set
+ */
+ public Set<VlanId> ceVlanIdSet() {
+ return ceVlanIdSet;
+ }
+
+ /**
+ * Returns the first non-null BWP of the UNI found - used mainly for service-specific UNIs.
+ * Note: The Service-specific UNI representation will only have one BWP
+ *
+ * @return first non-null BWP of the UNI
+ */
+ public CarrierEthernetBandwidthProfile bwp() {
+
+ for (CarrierEthernetBandwidthProfile.Type bwpType : CarrierEthernetBandwidthProfile.Type.values()) {
+ if (!(this.bwpMap.get(bwpType).isEmpty())) {
+ return bwpMap.get(bwpType).entrySet().iterator().next().getValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a collection of all BWPs of the UNI
+ *
+ * @return all BWPs of the UNI
+ */
+ public Collection<CarrierEthernetBandwidthProfile> bwps() {
+
+ for (CarrierEthernetBandwidthProfile.Type bwpType : CarrierEthernetBandwidthProfile.Type.values()) {
+ if (!(this.bwpMap.get(bwpType).isEmpty())) {
+ return bwpMap.get(bwpType).values();
+ }
+ }
+ // Return an empty collection if no BWPs exist
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns UNI capacity in bps.
+ *
+ * @return UNI capacity
+ */
+ public Bandwidth capacity() {
+ return capacity;
+ }
+
+ /**
+ * Sets UNI string identifier.
+ *
+ * @param uniId the UNI string identifier to set
+ */
+ public void setId(String uniId) {
+ this.uniId = uniId;
+ }
+
+ /**
+ * Sets UNI string config identifier.
+ *
+ * @param uniCfgId the UNI string config identifier to set
+ */
+ public void setCfgId(String uniCfgId) {
+ this.uniCfgId = uniCfgId;
+ }
+
+ @Override
+ public String toString() {
+
+ return toStringHelper(this)
+ .add("id", uniId)
+ .add("cfgId", uniCfgId)
+ .add("type", type)
+ .add("ceVlanIds", ceVlanIdSet)
+ .add("capacity", capacity)
+ .add("usedCapacity", usedCapacity)
+ .add("bandwidthProfiles", this.bwps()).toString();
+ }
+
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/package-info.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/package-info.java
new file mode 100644
index 0000000..4b5dc67
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementation of carrier ethernet service generation.
+ */
+package org.onosproject.ecord.carrierethernet.app;
\ No newline at end of file
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetCreateServiceCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetCreateServiceCommand.java
new file mode 100644
index 0000000..ae2d2cc
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetCreateServiceCommand.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.cli;
+
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Bandwidth;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetBandwidthProfile;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetManager;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetService;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetUni;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * CLI command for generating CE services.
+ */
+@Command(scope = "onos", name = "ce-service-create",
+ description = "Carrier Ethernet service creation command.")
+public class CarrierEthernetCreateServiceCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "argServiceCfgId",
+ description = "Service configuration ID", required = true, multiValued = false)
+ String argServiceCfgId = null;
+ @Argument(index = 1, name = "argServiceType", description =
+ "Service type (defaults to POINT_TO_POINT or MULTIPOINT_TO_MULTIPOINT, depending on number of UNIs)",
+ required = false, multiValued = false)
+ String argServiceType = null;
+ @Argument(index = 2, name = "argFirstUni", description =
+ "First UNI in list (if point to multipoint, this is the root)", required = true, multiValued = false)
+ String argFirstUni = null;
+ @Argument(index = 3, name = "argUniList",
+ description = "List of remaining UNIs (if point to multipoint, these are the leaves)",
+ required = true, multiValued = true)
+ List<String> argUniList = Lists.newArrayList();
+ @Option(name = "-v", aliases = "--cevlan", description = "CE-VLAN ID (applied to all UNIs)",
+ required = false, multiValued = false)
+ String argCeVlanId = null;
+ @Option(name = "-id", aliases = "--service-id", description = "The ID of a service to be updated" +
+ " (if service does not exist, a new service will be installed)", required = false, multiValued = false)
+ String argServiceId = null;
+ @Option(name = "-c", aliases = "--cir", description = "The CIR in Mbps", required = false, multiValued = false)
+ String argCir = "0";
+ @Option(name = "-e", aliases = "--eir", description = "The EIR in Mbps", required = false, multiValued = false)
+ String argEir = "0";
+ @Option(name = "-cbs", aliases = "--cbs", description = "The CBS in Bytes", required = false, multiValued = false)
+ String argCbs = "0";
+ @Option(name = "-ebs", aliases = "--ebs", description = "The EBS in Bytes", required = false, multiValued = false)
+ String argEbs = "0";
+
+ // TODO: Add further arguments for VLAN tag preservation, CoS preservation etc.
+
+ @Override
+ protected void execute() {
+
+ CarrierEthernetManager cem = get(CarrierEthernetManager.class);
+
+ CarrierEthernetService service = new CarrierEthernetService(argServiceId, argServiceCfgId,
+ generateServiceType(), generateUniSet());
+
+ cem.establishConnectivity(service);
+ }
+
+ /**
+ * Return the CE-VLAN ID for the CE service based on the CLI-supplied argument.
+ *
+ * @return CE-VLAN ID for the CE service
+ */
+ VlanId generateCeVlanId() {
+ return ((argCeVlanId == null) ? null : VlanId.vlanId(Short.parseShort(argCeVlanId)));
+ }
+
+ /**
+ * Return the CE service type based on the CLI-supplied arguments.
+ *
+ * @return the CE service type
+ */
+ CarrierEthernetService.Type generateServiceType() {
+ if (argServiceType == null) {
+ return ((argUniList.size() > 2) ?
+ CarrierEthernetService.Type.MULTIPOINT_TO_MULTIPOINT : CarrierEthernetService.Type.POINT_TO_POINT);
+ } else {
+ return CarrierEthernetService.Type.valueOf(argServiceType);
+ }
+ }
+
+ /**
+ * Return the BW profile type based on the CLI-supplied arguments.
+ *
+ * @return the BWP profile type
+ */
+ CarrierEthernetBandwidthProfile.Type generateBandwidthProfileType() {
+ // TODO: Add the CoS BW profile case
+ return ((argCeVlanId == null) ?
+ CarrierEthernetBandwidthProfile.Type.INTERFACE : CarrierEthernetBandwidthProfile.Type.EVC);
+ }
+
+ /**
+ * Return the BW profile id based on the CLI-supplied arguments.
+ *
+ * @param uniId the UNI id
+ * @return the BW profile id
+ */
+ String generateBandwidthProfileId(String uniId) {
+ // TODO: Add the CoS BW profile case
+ return ((argCeVlanId == null) ? uniId : argServiceCfgId);
+ }
+
+ /**
+ * Return the set of UNIs for the CE service based on the CLI-supplied arguments.
+ *
+ * @return the set of UNIs for the CE service
+ */
+ Set<CarrierEthernetUni> generateUniSet() {
+
+ Set<CarrierEthernetUni> uniSet = new HashSet<>();
+
+ CarrierEthernetService.Type serviceType = generateServiceType();
+
+ // We assume that first UNI supplied is always root
+ uniSet.add(new CarrierEthernetUni(ConnectPoint.deviceConnectPoint(argFirstUni), null,
+ CarrierEthernetUni.Type.ROOT, generateCeVlanId(),
+ new CarrierEthernetBandwidthProfile(
+ generateBandwidthProfileId(argFirstUni),
+ null,
+ generateBandwidthProfileType(),
+ Bandwidth.mbps(Double.parseDouble(argCir)),
+ Bandwidth.mbps(Double.parseDouble(argEir)),
+ Long.parseLong(argCbs),
+ Long.parseLong(argEbs)
+ )));
+
+ final CarrierEthernetUni.Type uniType;
+ // For E-Line and E-LAN all UNIs are roots. For E-Tree all UNIs are leafs except from one
+ uniType = ((serviceType == CarrierEthernetService.Type.ROOT_MULTIPOINT) ?
+ CarrierEthernetUni.Type.LEAF : CarrierEthernetUni.Type.ROOT);
+
+ argUniList.forEach(argUni -> uniSet.add(new CarrierEthernetUni(ConnectPoint.deviceConnectPoint(argUni), null,
+ uniType, generateCeVlanId(),
+ new CarrierEthernetBandwidthProfile(
+ generateBandwidthProfileId(argUni),
+ null,
+ generateBandwidthProfileType(),
+ Bandwidth.mbps(Double.parseDouble(argCir)),
+ Bandwidth.mbps(Double.parseDouble(argEir)),
+ Long.parseLong(argCbs),
+ Long.parseLong(argEbs)
+ ))));
+
+ return uniSet;
+ }
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetListServicesCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetListServicesCommand.java
new file mode 100644
index 0000000..ee1dbc1
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetListServicesCommand.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetManager;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetService;
+import org.onosproject.cli.AbstractShellCommand;
+
+import java.util.Collection;
+
+/**
+ * CLI command for listing all installed CE services.
+ */
+@Command(scope = "onos", name = "ce-service-list",
+ description = "Lists all Carrier Ethernet services.")
+public class CarrierEthernetListServicesCommand extends AbstractShellCommand {
+
+ @Override
+ protected void execute() {
+ CarrierEthernetManager cem = get(CarrierEthernetManager.class);
+ printServices(cem.serviceMap().values());
+ }
+
+ private void printServices(Collection<CarrierEthernetService> services) {
+ services.forEach(service -> print(" %s", service));
+ }
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetListUnisCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetListUnisCommand.java
new file mode 100644
index 0000000..71ca648
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetListUnisCommand.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetManager;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetUni;
+
+import java.util.Collection;
+
+/**
+ * CLI command for listing all CE UNIs.
+ */
+@Command(scope = "onos", name = "ce-uni-list",
+ description = "Lists all Carrier Ethernet UNIs.")
+public class CarrierEthernetListUnisCommand extends AbstractShellCommand {
+
+ @Override
+ protected void execute() {
+ CarrierEthernetManager cem = get(CarrierEthernetManager.class);
+ // Populate global UNI map
+ cem.addGlobalUnis();
+ printUnis(cem.getUniMap().values());
+ }
+
+ private void printUnis(Collection<CarrierEthernetUni> unis) {
+ unis.forEach(uni -> print(" %s", uni));
+ }
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetRemoveAllServicesCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetRemoveAllServicesCommand.java
new file mode 100644
index 0000000..0232e82
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetRemoveAllServicesCommand.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetManager;
+import org.onosproject.cli.AbstractShellCommand;
+
+/**
+ * CLI command for removing all installed CE services.
+ */
+@Command(scope = "onos", name = "ce-service-remove-all",
+ description = "Carrier Ethernet all services removal.")
+public class CarrierEthernetRemoveAllServicesCommand extends AbstractShellCommand {
+
+ @Override
+ protected void execute() {
+ CarrierEthernetManager cem = get(CarrierEthernetManager.class);
+ cem.removeAllServices();
+ }
+}
\ No newline at end of file
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetRemoveServiceCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetRemoveServiceCommand.java
new file mode 100644
index 0000000..1c088cb
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetRemoveServiceCommand.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetManager;
+import org.onosproject.cli.AbstractShellCommand;
+
+/**
+ * CLI command for removing a specific installed CE service.
+ */
+@Command(scope = "onos", name = "ce-service-remove",
+ description = "Carrier Ethernet service removal command.")
+public class CarrierEthernetRemoveServiceCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "argServiceId", description = "Service ID", required = true, multiValued = false)
+ String argServiceId = null;
+
+ @Override
+ protected void execute() {
+ CarrierEthernetManager cem = get(CarrierEthernetManager.class);
+ cem.removeService(argServiceId);
+ }
+}
\ No newline at end of file
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetServiceIdCompleter.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetServiceIdCompleter.java
new file mode 100644
index 0000000..7ebc192
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetServiceIdCompleter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 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.carrierethernet.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.ecord.carrierethernet.app.CarrierEthernetManager;
+import org.onosproject.cli.AbstractShellCommand;
+
+import java.util.List;
+import java.util.SortedSet;
+
+public class CarrierEthernetServiceIdCompleter implements Completer {
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+
+ StringsCompleter delegate = new StringsCompleter();
+ CarrierEthernetManager cem = AbstractShellCommand.get(CarrierEthernetManager.class);
+ SortedSet<String> strings = delegate.getStrings();
+ cem.serviceMap().keySet().forEach(serviceId -> strings.add(serviceId));
+ return delegate.complete(buffer, cursor, candidates);
+ }
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetServiceTypeCompleter.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetServiceTypeCompleter.java
new file mode 100644
index 0000000..0499cf8
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetServiceTypeCompleter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 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.carrierethernet.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+
+import java.util.List;
+import java.util.SortedSet;
+
+public class CarrierEthernetServiceTypeCompleter implements Completer {
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+
+ StringsCompleter delegate = new StringsCompleter();
+
+ SortedSet<String> strings = delegate.getStrings();
+
+ strings.add("POINT_TO_POINT");
+ strings.add("MULTIPOINT_TO_MULTIPOINT");
+ strings.add("ROOT_MULTIPOINT");
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
+
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetUniCompleter.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetUniCompleter.java
new file mode 100644
index 0000000..f8d6f03
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/CarrierEthernetUniCompleter.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+package org.onosproject.ecord.carrierethernet.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.LinkService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * UNI ConnectPoint completer.
+ */
+public class CarrierEthernetUniCompleter implements Completer {
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+
+ // TODO: Add memory
+
+ StringsCompleter delegate = new StringsCompleter();
+
+ LinkService linkService = AbstractShellCommand.get(LinkService.class);
+ DeviceService service = AbstractShellCommand.get(DeviceService.class);
+
+ // Generate the device ID/port number identifiers
+ for (Device device : service.getDevices()) {
+ SortedSet<String> strings = delegate.getStrings();
+ for (Port port : service.getPorts(device.id())) {
+ // Consider only physical ports which are currently active
+ if (!port.number().isLogical() && port.isEnabled()) {
+ String cpString = device.id().toString() + "/" + port.number();
+ ConnectPoint cp = ConnectPoint.deviceConnectPoint(cpString);
+ // Add the generated connect point only if it doesn't belong to any link
+ // and if the device is a packet switch
+ if (linkService.getEgressLinks(cp).isEmpty() && linkService.getIngressLinks(cp).isEmpty() &&
+ device.type().equals(Device.Type.SWITCH)) {
+ strings.add(cpString);
+ }
+ }
+ }
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/package-info.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/package-info.java
new file mode 100644
index 0000000..32e8e79
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * CLI implementation for requesting carrier ethernet services.
+ */
+package org.onosproject.ecord.carrierethernet.cli;
\ No newline at end of file
diff --git a/ecord/carrierethernet/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/ecord/carrierethernet/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..886a712
--- /dev/null
+++ b/ecord/carrierethernet/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,63 @@
+<!--
+ ~ 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.carrierethernet.cli.CarrierEthernetCreateServiceCommand"/>
+ <completers>
+ <ref component-id="placeholderCompleter"/>
+ <ref component-id="carrierEthernetServiceTypeCompleter"/>
+ <ref component-id="carrierEthernetUniCompleter"/>
+ <ref component-id="carrierEthernetUniCompleter"/>
+ </completers>
+ </command>
+ </command-bundle>
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.ecord.carrierethernet.cli.CarrierEthernetRemoveServiceCommand"/>
+ <completers>
+ <ref component-id="carrierEthernetServiceIdCompleter"/>
+ </completers>
+ </command>
+ </command-bundle>
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.ecord.carrierethernet.cli.CarrierEthernetRemoveAllServicesCommand"/>
+ </command>
+ </command-bundle>
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.ecord.carrierethernet.cli.CarrierEthernetListServicesCommand"/>
+ </command>
+ </command-bundle>
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.ecord.carrierethernet.cli.CarrierEthernetListUnisCommand"/>
+ </command>
+ </command-bundle>
+
+ <bean id="placeholderCompleter" class="org.onosproject.cli.PlaceholderCompleter"/>
+ <bean id="carrierEthernetServiceTypeCompleter" class="org.onosproject.ecord.carrierethernet.cli.CarrierEthernetServiceTypeCompleter"/>
+ <bean id="carrierEthernetServiceIdCompleter" class="org.onosproject.ecord.carrierethernet.cli.CarrierEthernetServiceIdCompleter"/>
+ <bean id="carrierEthernetUniCompleter" class="org.onosproject.ecord.carrierethernet.cli.CarrierEthernetUniCompleter"/>
+ <bean id="connectPointCompleter" class="org.onosproject.cli.net.ConnectPointCompleter"/>
+
+</blueprint>
diff --git a/pom.xml b/pom.xml
index aa196fe..fb519c9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
<module>tvue</module>
<module>uiref</module>
<module>ecord/co</module>
+ <module>ecord/carrierethernet</module>
<module>ecord/metro</module>
</modules>