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>