Major refactoring to always link EVCs to FCs + adding EVC-to-FC fragmentation

- Making EVCs to always be composed of FCs
- Subclassing EVCs and FCs from abstract CarrierEthernetConnection class
- Refactored CarrierEthernetProvisioner to only work with FCs
- Adding basing orchestration functionality in CarrierEthernetManager to create set of FCs out of an EVC based on LTPs in topology
- Automatically assign vlanIds to FCs and corresponding S-TAGs to INNIs of neighboring FCs
- Allowing setting forwarding for single-device FCs
- Making sure that INNIs always appear in pairs
- Minor modifications in some CLI commands
- Getting rid of the ActiveState enum in CarrierEthernetConnection.java and doing some relevant refactoring
- Renaming addNi/removeNi/validateNi methods in CarrierEthernetNetworkInterface.java

Change-Id: I3e4972bd792585a05912943cabb45becfe2e82a4
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetConnection.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetConnection.java
new file mode 100644
index 0000000..c847480
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetConnection.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ecord.carrierethernet.app;
+
+import java.time.Duration;
+
+/**
+ * Representation of an abstract CE connection (meant to be extended by the EVC and FC classes).
+ */
+public abstract class CarrierEthernetConnection {
+
+    public enum Type {
+
+        POINT_TO_POINT("Point_To_Point"),
+        MULTIPOINT_TO_MULTIPOINT("Multipoint_To_Multipoint"),
+        ROOT_MULTIPOINT("Root_Multipoint");
+
+        private String value;
+
+        Type(String value) {
+            this.value = value;
+        }
+
+        @Override
+        public String toString() {
+            return value;
+        }
+
+        public static Type fromString(String value) {
+            if (value != null) {
+                for (Type b : Type.values()) {
+                    if (value.equals(b.value)) {
+                        return b;
+                    }
+                }
+            }
+            throw new IllegalArgumentException("Type " + value + " is not valid");
+        }
+    }
+
+    public enum State {
+
+        ACTIVE("Active"),
+        INACTIVE("Inactive"),
+        PARTIAL("Partial");
+
+        private String value;
+
+        State(String value) {
+            this.value = value;
+        }
+
+        @Override
+        public String toString() {
+            return value;
+        }
+
+        public static State fromString(String value) {
+            if (value != null) {
+                for (State b : State.values()) {
+                    if (value.equals(b.value)) {
+                        return b;
+                    }
+                }
+            }
+            throw new IllegalArgumentException("State " + value + " is not valid");
+        }
+    }
+
+    // TODO: Use the ONOS built-in identifier??
+    protected String id;
+    protected String cfgId;
+    protected Type type;
+    protected State state;
+    protected Duration maxLatency;
+
+    private static final Duration DEFAULT_MAX_LATENCY = Duration.ofMillis(50);
+
+    // Note: id should be provided only when updating an existing connection
+    public CarrierEthernetConnection(String id, String cfgId, Type type, Duration maxLatency) {
+        this.id = id;
+        this.cfgId = cfgId;
+        this.type = type;
+        this.state = State.INACTIVE;
+        this.maxLatency = maxLatency == null? DEFAULT_MAX_LATENCY: maxLatency;
+    }
+
+    /**
+     * Returns connection identifier.
+     *
+     * @return connection identifier
+     */
+    public String id() {
+        return id;
+    }
+
+    /**
+     * Returns connection config identifier.
+     *
+     * @return connection config identifier
+     */
+    public String cfgId() {
+        return cfgId;
+    }
+
+    /**
+     * Returns type of connection.
+     *
+     * @return type of connection
+     */
+    public Type type() {
+        return type;
+    }
+
+    /**
+     * Returns connectivity state of the connection.
+     *
+     * @return connectivity state
+     */
+    public State state() { return state; }
+
+    /**
+     * Indicates whether the connection is active or partially active.
+     *
+     * @return connectivity state
+     */
+    public boolean isActive() {
+        return !state.equals(State.INACTIVE);
+    }
+
+    /**
+     * Returns maximum latency constraint.
+     *
+     * @return maximum latency constraint
+     */
+    public Duration maxLatency() {
+        return maxLatency;
+    }
+
+    /**
+     * Sets connection identifier.
+     *
+     * @param id the connection identifier to set
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Sets connection config identifier.
+     *
+     * @param cfgId connection config identifier
+     */
+    public void setCfgId(String cfgId) {
+        this.cfgId = cfgId;
+    }
+
+    /**
+     * Sets the connectivity state of the connection.
+     *
+     * @param state the connectivity state to set
+     */
+    public void setState(State state) { this.state = state; }
+
+    /**
+     * Sets the type of connection.
+     *
+     * @param type type of connection to set
+     */
+    public void setType(Type type) { this.type = type; }
+
+    /**
+     * Sets maximum latency constraint.
+     *
+     * @param maxLatency the maximum latency constraint to set
+     */
+    public void setMaxLatency(Duration maxLatency) {
+        this.maxLatency = maxLatency;
+    }
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetEnni.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetEnni.java
index d7b4d95..68886f5 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetEnni.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetEnni.java
@@ -33,14 +33,17 @@
  * 1. As a global ENNI descriptor containing one or more BW profiles
  * 2. As a service-specific ENNI descriptor containing a single S-VLAN tag and including a type (e.g. hub, spoke)
  */
-public class CarrierEthernetEnni extends CarrierEthernetNetworkInterface {
+public class CarrierEthernetEnni extends CarrierEthernetNetworkInterface <CarrierEthernetEnni> {
 
     private final Logger log = getLogger(getClass());
 
     public enum Role {
 
         HUB("Hub"),
-        SPOKE("Spoke");
+        SPOKE("Spoke"),
+        // FIXME: Remove these after LTP-NI role mapping is fixed
+        ROOT("Root"),
+        LEAF("Leaf");
 
         private String value;
 
@@ -76,13 +79,12 @@
     }
 
     /**
-     * Adds the resources associated with an EVC-specific ENNI to a global ENNI.
+     * Adds the resources associated with an FC-specific ENNI to a global ENNI.
      *
-     * @param enni the EVC ENNI to be added
+     * @param enni the FC-specific ENNI to be added
      */
-    // TODO: Make these methods abstract
-    public void addEvcEnni(CarrierEthernetEnni enni) {
-
+    @Override
+    public void addEcNi(CarrierEthernetEnni enni) {
         // Add S-VLAN ID
         if (enni.sVlanId() != VlanId.NONE) {
             this.sVlanIdSet.add(enni.sVlanId());
@@ -92,26 +94,26 @@
     }
 
     /**
-     * Removes the resources associated with a service-specific ENNI from a global ENNI.
+     * Removes the resources associated with an FC-specific ENNI from a global ENNI.
      *
-     * @param enni the service ENNI to be added
+     * @param enni the FC-specific ENNI to be removed
      */
-    public void removeEvcEnni(CarrierEthernetEnni enni) {
-
+    @Override
+    public void removeEcNi(CarrierEthernetEnni enni) {
         // Remove UNI CE-VLAN ID
         sVlanIdSet.remove(enni.sVlanId());
-
         // Redundant check - should be avoided by check in validateBwp
         this.usedCapacity = Bandwidth.bps(Math.max(this.usedCapacity.bps() - enni.usedCapacity().bps(), 0));
     }
 
     /**
-     * Validates whether an EVC-specific ENNI is compatible with a global ENNI.
+     * Validates whether an FC-specific ENNI is compatible with the corresponding global ENNI.
      *
-     * @param enni the EVC-specific UNI
-     * @return boolean value indicating whether the UNIs are compatible
+     * @param enni the FC-specific ENNI
+     * @return boolean value indicating whether the ENNIs are compatible
      */
-    public boolean validateEvcEnni(CarrierEthernetEnni enni) {
+    @Override
+    public boolean validateEcNi(CarrierEthernetEnni enni) {
 
         // Check if the S-VLAN ID of the ENNI is already included in global ENNI
         if (enni.sVlanId() != VlanId.NONE) {
@@ -180,6 +182,16 @@
         return sVlanIdSet;
     }
 
+    // FIXME: Find a better way to implement this method
+    /**
+     * Sets the S-VLAN id associated with an FC INNI.
+     *
+     * @param sVlanId S-VLAN id to set
+     */
+    public void setSVlanId(VlanId sVlanId) {
+        sVlanIdSet.add(sVlanId);
+    }
+
     @Override
     public String toString() {
 
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetForwardingConstruct.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetForwardingConstruct.java
index 55ce9cb..c884bf9 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetForwardingConstruct.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetForwardingConstruct.java
@@ -15,133 +15,73 @@
  */
 package org.onosproject.ecord.carrierethernet.app;
 
+import com.google.common.annotations.Beta;
+import org.onlab.packet.VlanId;
+import org.onosproject.newoptical.api.OpticalConnectivityId;
 import org.onosproject.newoptical.api.OpticalPathEvent;
-import org.slf4j.Logger;
 
 import java.time.Duration;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
-import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Representation of a CE Forwarding Construct.
  */
-public class CarrierEthernetForwardingConstruct {
+public class CarrierEthernetForwardingConstruct extends CarrierEthernetConnection {
 
-    private final Logger log = getLogger(getClass());
-
-    public enum State {
-
-        ACTIVE("Active"),
-        INACTIVE("Inactive");
-
-        private String value;
-
-        State(String value) {
-            this.value = value;
-        }
-
-        @Override
-        public String toString() {
-            return value;
-        }
-
-        public static State fromString(String value) {
-            if (value != null) {
-                for (State b : State.values()) {
-                    if (value.equals(b.value)) {
-                        return b;
-                    }
-                }
-            }
-            throw new IllegalArgumentException("State " + value + " is not valid");
-        }
-    }
-
-    public enum ActiveState {
-
-        FULL("Full"),
-        PARTIAL("Partial");
-
-        private String value;
-
-        ActiveState(String value) {
-            this.value = value;
-        }
-
-        @Override
-        public String toString() {
-            return value;
-        }
-    }
-
-    protected String fcId;
-    protected String fcCfgId;
-    protected String evcId;
-    protected CarrierEthernetVirtualConnection.Type evcType;
-    protected Set<CarrierEthernetLogicalTerminationPoint> ltpSet;
-    protected CarrierEthernetForwardingConstruct.State state;
-    protected CarrierEthernetForwardingConstruct.ActiveState activeState;
-    protected Duration latency;
-    protected CarrierEthernetMetroConnectivity metroConnectivity;
-    protected boolean congruentPaths;
-
-    // FIXME: Find a better way
-    protected CarrierEthernetVirtualConnection evcLite;
+    private Set<CarrierEthernetLogicalTerminationPoint> ltpSet;
+    private VlanId vlanId;
+    private VlanId transportVlanId;
+    private CarrierEthernetMetroConnectivity metroConnectivity;
+    private boolean congruentPaths;
+    protected AtomicInteger refCount;
 
     // 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);
-
-    // TODO: Maybe fcCfgId and evcId are not needed?
     // Note: fcId should be provided only when updating an existing FC
-    public CarrierEthernetForwardingConstruct(String fcId, String fcCfgId,
-                                              String evcId, CarrierEthernetVirtualConnection.Type evcType,
-                                              Set<CarrierEthernetLogicalTerminationPoint> ltpSet) {
-        this.fcId = fcId;
-        this.fcCfgId = (fcCfgId == null? fcId : fcCfgId);
-        this.evcId = evcId;
-        this.evcType = evcType;
-        this.state = State.INACTIVE;
+    public CarrierEthernetForwardingConstruct(String id, String cfgId, Type type,
+                                              Set<CarrierEthernetLogicalTerminationPoint> ltpSet,
+                                              Duration maxLatency) {
+        super(id, cfgId, type, maxLatency);
         this.ltpSet = new HashSet<>(ltpSet);
-        this.congruentPaths = CONGRUENT_PATHS;
-        this.latency = DEFAULT_LATENCY;
+        this.vlanId = null;
+        this.transportVlanId = null;
         this.metroConnectivity = new CarrierEthernetMetroConnectivity(null, OpticalPathEvent.Type.PATH_REMOVED);
-
-        // FIXME: This is (probably) just a temporary solution
-        // Create a lightweight EVC out of the FC which can be used with existing methods
-        Set<CarrierEthernetNetworkInterface> niSet = new HashSet<>();
-        ltpSet.forEach(ltp -> {
-            if (ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
-                niSet.add(ltp.ni());
-            }
-        });
-        this.evcLite = new CarrierEthernetVirtualConnection(fcId, fcCfgId, evcType, null, niSet);
-    }
-
-    // TODO: Create constructor using the End-to-End service and a set of LTPs
-
-    public String toString() {
-
-        return toStringHelper(this)
-                .add("id", fcId)
-                .add("cfgId", fcCfgId)
-                .add("evcId", evcId)
-                .add("evcType", evcType)
-                .add("metroConnectId", (metroConnectivity.id() == null ? "null" : metroConnectivity.id().id()))
-                .add("LTPs", ltpSet).toString();
+        this.congruentPaths = CONGRUENT_PATHS;
+        this.refCount = new AtomicInteger();
     }
 
     /**
-     * Returns the id of the FC.
+     * Returns Vlan id.
      *
-     * @return id of the FC
+     * @return Vlan id
      */
-    public String id() {
-        return fcId;
+    public VlanId vlanId() {
+        return vlanId;
+    }
+
+    /**
+     * Returns Transport Vlan ID.
+     *
+     * @return Transport Vlan ID.
+     */
+    @Beta
+    public VlanId transportVlanId() {
+        return transportVlanId;
+    }
+
+    /**
+     * Gets metro connectivity id.
+     *
+     * @return the metro connectivity of the service
+     */
+    public CarrierEthernetMetroConnectivity metroConnectivity() {
+        return this.metroConnectivity;
     }
 
     /**
@@ -154,46 +94,53 @@
     }
 
     /**
-     * Returns the type of the EVC associated with the FC.
+     * Returns the set of UNIs associated with the FC.
      *
-     * @return type of associated EVC
+     * @return set of UNIs associated with the FC
      */
-    public CarrierEthernetVirtualConnection.Type evcType() {
-        return evcType;
+    public Set<CarrierEthernetUni> uniSet() {
+        // FIXME: Find a more efficient way to get the FC UNIs
+        return ltpSet
+                .stream()
+                .filter(ltp -> ltp.type().equals(CarrierEthernetNetworkInterface.Type.UNI))
+                .map(ltp -> (CarrierEthernetUni) ltp.ni()).collect(Collectors.toSet());
     }
 
     /**
-     * Returns connectivity state of the FC.
+     * Returns true if FC requires congruent paths.
      *
-     * @return connectivity state
+     * @return true if congruent paths required
      */
-    public State state() {
-        return state;
+    public boolean congruentPaths() {
+        return congruentPaths;
     }
 
     /**
-     * Returns active connectivity state of the FC.
+     * Returns counter with the number of references (from EVCs) to the particular FC.
      *
-     * @return active connectivity state
+     * @return number of references counter
      */
-    public ActiveState activeState() {
-        return activeState;
+    public AtomicInteger refCount() {
+        return refCount;
     }
 
     /**
-     * Returns the "EVC" associated with FC.
+     * Sets the vlanId to be used by the FC.
      *
-     * @return the "EVC" associated with FC
+     * @param vlanId the vlanId to set
      */
-    public CarrierEthernetVirtualConnection evcLite() { return evcLite; }
+    public void setVlanId(VlanId vlanId) {
+        this.vlanId = vlanId;
+    }
 
     /**
-     * Sets the id of the FC.
+     * Sets the vlanId to be used by the transport part of the FC.
      *
-     * @param id the id to set to the FC
+     * @param vlan the vlanId to set
      */
-    public void setId(String id) {
-        this.fcId = id;
+    @Beta
+    public void setTransportVlanId(VlanId vlan) {
+        this.transportVlanId = vlan;
     }
 
     /**
@@ -206,17 +153,33 @@
     }
 
     /**
-     * Sets the connectivity state of the FC.
+     * Sets metro connectivity id.
      *
-     * @param state the connectivity state to set
+     * @param id the metro connectivity identifier to set
      */
-    public void setState(State state) { this.state = state; }
+    public void setMetroConnectivityId(OpticalConnectivityId id) {
+        this.metroConnectivity.setId(id);
+    }
 
     /**
-     * Sets the active connectivity state of the FC.
+     * Sets metro connectivity status.
      *
-     * @param activeState the active connectivity state to set
+     * @param status the metro connectivity status
      */
-    public void setActiveState(ActiveState activeState) { this.activeState = activeState; }
+    public void setMetroConnectivityStatus(OpticalPathEvent.Type status) {
+        this.metroConnectivity.setStatus(status);
+    }
 
+    public String toString() {
+
+        return toStringHelper(this)
+                .add("id", id)
+                .add("cfgId", cfgId)
+                .add("type", type)
+                .add("vlanId", vlanId)
+                .add("transportVlanId", transportVlanId)
+                .add("metroConnectId", (metroConnectivity.id() == null ? "null" : metroConnectivity.id().id()))
+                .add("refCount", refCount)
+                .add("LTPs", ltpSet).toString();
+    }
 }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetGenericNi.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetGenericNi.java
index 2ddba2e..0795e01 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetGenericNi.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetGenericNi.java
@@ -30,9 +30,9 @@
 
 /**
  * Representation of a Generic Carrier Ethernet NI.
- * Class is only mean to be used for establishing forwarding in CarrierEthernetPacketNodeManagers
+ * Class is only meant to be used for establishing forwarding in CarrierEthernetPacketNodeManagers
  */
-public class CarrierEthernetGenericNi extends CarrierEthernetNetworkInterface {
+public class CarrierEthernetGenericNi extends CarrierEthernetNetworkInterface <CarrierEthernetGenericNi> {
 
     public enum Role {
 
@@ -78,6 +78,31 @@
         return null;
     }
 
+    /**
+     * Dummy implementation of abstract method (for generic NI type there is no concept of EVC vs. global NIs).
+     *
+     * @param gni a generic NI
+     */
+    @Override
+    public void addEcNi(CarrierEthernetGenericNi gni) {}
+
+    /**
+     * Dummy implementation of abstract method (for generic NI type there is no concept of EVC vs. global NIs).
+     *
+     * @param gni a generic NI
+     */
+    @Override
+    public void removeEcNi(CarrierEthernetGenericNi gni) {}
+
+    /**
+     * Dummy implementation of abstract method (for generic NI type there is no concept of EVC vs. global NIs).
+     *
+     * @param gni a generic NI
+     * @return true
+     */
+    @Override
+    public boolean validateEcNi(CarrierEthernetGenericNi gni) { return true; }
+
     @Override
     public String toString() {
 
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetInni.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetInni.java
index c876244..64c8f4f 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetInni.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetInni.java
@@ -34,7 +34,7 @@
  * 1. As a global INNI descriptor containing one or more BW profiles
  * 2. As a service-specific INNI descriptor containing a single S-VLAN tag and including a type (e.g. hub, spoke)
  */
-public class CarrierEthernetInni extends CarrierEthernetNetworkInterface {
+public class CarrierEthernetInni extends CarrierEthernetNetworkInterface <CarrierEthernetInni> {
 
     private final Logger log = getLogger(getClass());
 
@@ -43,7 +43,10 @@
         HUB("Hub"),
         // FIXME: Remove that after hackathon?
         TRUNK("Trunk"),
-        SPOKE("Spoke");
+        SPOKE("Spoke"),
+        // FIXME: Remove these after LTP-NI role mapping is fixed
+        ROOT("Root"),
+        LEAF("Leaf");
 
         private String value;
 
@@ -79,13 +82,12 @@
     }
 
     /**
-     * Adds the resources associated with an EVC-specific INNI to a global INNI.
+     * Adds the resources associated with an FC-specific INNI to a global INNI.
      *
-     * @param inni the EVC INNI to be added
+     * @param inni the FC-specific INNI to be added
      */
-    // TODO: Make these methods abstract
-    public void addEvcInni(CarrierEthernetInni inni) {
-
+    @Override
+    public void addEcNi(CarrierEthernetInni inni) {
         // Add S-VLAN ID
         if (inni.sVlanId() != VlanId.NONE) {
             this.sVlanIdSet.add(inni.sVlanId());
@@ -96,26 +98,26 @@
     }
 
     /**
-     * Removes the resources associated with a service-specific INNI from a global INNI.
+     * Removes the resources associated with an FC-specific INNI from a global INNI.
      *
-     * @param inni the service INNI to be added
+     * @param inni the FC-specific INNI to be removed
      */
-    public void removeEvcInni(CarrierEthernetInni inni) {
-
+    @Override
+    public void removeEcNi(CarrierEthernetInni inni) {
         // Remove UNI CE-VLAN ID
         sVlanIdSet.remove(inni.sVlanId());
-
         // Redundant check - should be avoided by check in validateBwp
         this.usedCapacity = Bandwidth.bps(Math.max(this.usedCapacity.bps() - inni.usedCapacity().bps(), 0));
     }
 
     /**
-     * Validates whether an EVC-specific INNI is compatible with a global INNI.
+     * Validates whether an FC-specific INNI is compatible with the corresponding global INNI.
      *
-     * @param inni the EVC-specific UNI
-     * @return boolean value indicating whether the UNIs are compatible
+     * @param inni the FC-specific INNI
+     * @return boolean value indicating whether the INNIs are compatible
      */
-    public boolean validateEvcInni(CarrierEthernetInni inni) {
+    @Override
+    public boolean validateEcNi(CarrierEthernetInni inni) {
 
         // Check if the S-VLAN ID of the INNI is already included in global INNI
         if (inni.sVlanId() != null) {
@@ -175,6 +177,16 @@
         return ImmutableSet.copyOf(sVlanIdSet);
     }
 
+    // FIXME: Find a better way to implement this method
+    /**
+     * Sets the S-VLAN id associated with an FC INNI.
+     *
+     * @param sVlanId S-VLAN id to set
+     */
+    public void setSVlanId(VlanId sVlanId) {
+        sVlanIdSet.add(sVlanId);
+    }
+
     /**
      * Returns INNI tpid - applicable only to service-specific INNIs.
      *
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetLogicalTerminationPoint.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetLogicalTerminationPoint.java
index ba564d4..cd4ccef 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetLogicalTerminationPoint.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetLogicalTerminationPoint.java
@@ -74,16 +74,22 @@
     }
 
     public CarrierEthernetLogicalTerminationPoint(ConnectPoint cp, String ltpCfgId,
-                                                  CarrierEthernetNetworkInterface.Type niType) {
+                                                  CarrierEthernetNetworkInterface.Type niType,
+                                                  CarrierEthernetLogicalTerminationPoint.Role role) {
         this.ltpId = cp.deviceId().toString() + "/" + cp.port().toString();
         this.ltpCfgId = (ltpCfgId == null ? this.ltpId : ltpCfgId);
-        // NOTE: Role is expected to be null for service-specific LTPs/NIs
+        this.role = role;
+        // NOTE: Role is expected to be null for global LTPs/NIs
+        // FIXME: Provide appropriate mapping between LTP and NI roles (e.g. ROOT -> HUB, LEAF -> SPOKE)
         if (niType.equals(CarrierEthernetNetworkInterface.Type.UNI)) {
-            this.ni = new CarrierEthernetUni(cp, ltpId, null, null, null);
+            CarrierEthernetUni.Role uniRole = (role == null) ? null : CarrierEthernetUni.Role.valueOf(role.name());
+            this.ni = new CarrierEthernetUni(cp, ltpId, uniRole, null, null);
         } else if (niType.equals(CarrierEthernetNetworkInterface.Type.INNI))  {
-            this.ni = new CarrierEthernetInni(cp, ltpId, null, null, null, null);
+            CarrierEthernetInni.Role inniRole = (role == null) ? null : CarrierEthernetInni.Role.valueOf(role.name());
+            this.ni = new CarrierEthernetInni(cp, ltpId, inniRole, null, null, null);
         } else if (niType.equals(CarrierEthernetNetworkInterface.Type.ENNI)) {
-            this.ni = new CarrierEthernetEnni(cp, ltpId, null, null, null, null);
+            CarrierEthernetEnni.Role enniRole = (role == null) ? null : CarrierEthernetEnni.Role.valueOf(role.name());
+            this.ni = new CarrierEthernetEnni(cp, ltpId, enniRole, null, null, null);
         }
     }
 
@@ -168,6 +174,15 @@
         this.ni = ni;
     }
 
+    /**
+     * Returns LTP role - applicable only to EVC- or FC-specific LTPs.
+     *
+     * @param role he LTP role to set
+     */
+    public void setRole(Role role) {
+        this.role = role;
+    }
+
     public String toString() {
 
         return toStringHelper(this)
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
index d22e8c6..a304fd0 100644
--- 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
@@ -16,6 +16,7 @@
 package org.onosproject.ecord.carrierethernet.app;
 
 import com.google.common.collect.Sets;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
@@ -26,11 +27,18 @@
 
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
 import org.onosproject.net.Port;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.link.LinkService;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.TopologyService;
 import org.slf4j.Logger;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -39,6 +47,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
+import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
 import static org.slf4j.LoggerFactory.getLogger;
 
 @Component(immediate = true)
@@ -54,11 +63,23 @@
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PathService pathService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TopologyService topologyService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CarrierEthernetProvisioner ceProvisioner;
 
     // Keeps track of the next S-VLAN tag the app will try to use
     private static short nextVlanId = 1;
 
+    // Keeps track of the next EVC id the app will try to use
+    // TODO: Use Identifier class instead
+    private static short nextEvcShortId = 1;
+
+    private boolean evcFragmentationEnabled = true;
+
     // TODO: Implement distributed store for EVCs
     // The installed EVCs
     private final Map<String, CarrierEthernetVirtualConnection> evcMap = new ConcurrentHashMap<>();
@@ -70,7 +91,7 @@
     // TODO: Implement distributed store for CE UNIs
     // The installed CE UNIs
     private final Map<String, CarrierEthernetUni> uniMap = new ConcurrentHashMap<>();
-    private final Set<String> removedUniSet = Sets.newConcurrentHashSet();;
+    private final Set<String> removedUniSet = Sets.newConcurrentHashSet();
 
     // TODO: Implement distributed store for CE LTPs
     // The installed CE LTPs
@@ -163,26 +184,23 @@
         // Make a copy of the provided EVC, since it may be modified
         CarrierEthernetVirtualConnection evc = originalEvc;
 
-        // Try to set a unique VLAN id for the EVC unless the EVC is being updated
-        // TODO: Add different connectivity types
-        if (evc.vlanId() == null) {
-            evc.setVlanId(generateVlanId());
-            if (evc.vlanId() == null) {
-                log.error("No available VLAN id found.");
-                return null;
-            }
+        // Try to set a unique numerical id for the EVC unless the EVC is being updated
+        // FIXME: Check again the EVC update case
+        evc.setShortId(generateEvcShortId());
+        if (evc.shortId()  == null) {
+            log.error("No available EVC id found.");
+            return null;
         }
 
+        // Generate and set unique FC id
+        evc.setId(generateEvcId(evc));
+
         // Verify that CE-VLAN ID is provided to either all UNIs or none and set the virtualEvc flag accordingly
         // Note: Checking also that all NIs are UNIs
         boolean isVirtual = false;
-        Iterator<CarrierEthernetNetworkInterface> it = evc.niSet().iterator();
+        Iterator<CarrierEthernetUni> it = evc.uniSet().iterator();
         while (it.hasNext()) {
-            CarrierEthernetNetworkInterface ni = it.next();
-            if (!ni.type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
-                log.error("All EVC NIs have to be of type UNI.");
-                return null;
-            }
+            CarrierEthernetUni ni = it.next();
             if (ni.ceVlanId() == VlanId.NONE && isVirtual) {
                 log.error("Could not validate the virtual status of the EVC.");
                 return null;
@@ -197,12 +215,15 @@
             evc.setId(generateEvcId(evc));
         }
 
-        Set<CarrierEthernetNetworkInterface> validatedUniSet = new HashSet<>();
+        Set<CarrierEthernetUni> validatedUniSet = new HashSet<>();
+
+        // TODO: Refactor according to the validateFc method
+        // Note: Cannot use the validateFc method here, because FCs can also be standalone
 
         // Check the UNIs of the EVC, possibly removing UNIs that are incompatible with existing global ones
-        it = evc.niSet().iterator();
+        it = evc.uniSet().iterator();
         while (it.hasNext()) {
-            CarrierEthernetUni uni = (CarrierEthernetUni) it.next();
+            CarrierEthernetUni uni = it.next();
             // Change the name of the UNI's BWP to the EVC name if it is an EVC BWP
             if (uni.bwp().type().equals(CarrierEthernetBandwidthProfile.Type.EVC)) {
                 uni.bwp().setId(evc.id());
@@ -211,7 +232,7 @@
             if (uniMap.keySet().contains(uni.id())) {
                 CarrierEthernetUni existingUni = uniMap.get(uni.id());
                 // Check if the EVC-specific UNI is compatible with the global one
-                if (!(existingUni.validateEvcUni(uni))) {
+                if (!(existingUni.validateEcNi(uni))) {
                     // If EVC is of ROOT_MULTIPOINT type and we have removed the root, return null
                     if (evc.type() == CarrierEthernetVirtualConnection.Type.ROOT_MULTIPOINT &&
                             uni.role() == CarrierEthernetUni.Role.ROOT) {
@@ -231,21 +252,23 @@
         }
 
         // Update the EVC UNI set, based on the validated UNIs
-        evc.setNiSet(validatedUniSet);
+        evc.setUniSet(validatedUniSet);
 
-        if (evc.niSet().size() > evc.maxNumUni()) {
+        // TODO: Check that an ROOT_MULTIPOINT EVC has at most one ROOT
+
+        if (evc.uniSet().size() > evc.maxNumUni()) {
             log.error("{} EVC can have at most {} UNIs.", evc.maxNumUni());
             return null;
         }
 
         if ((evc.type().equals(CarrierEthernetVirtualConnection.Type.ROOT_MULTIPOINT)
                 || evc.type().equals(CarrierEthernetVirtualConnection.Type.MULTIPOINT_TO_MULTIPOINT))
-                && (evc.niSet().size() < 2)) {
+                && (evc.uniSet().size() < 2)) {
             log.error("{} EVC requires at least two UNIs.", evc.type().name());
             return null;
         }
 
-        if (evc.type().equals(CarrierEthernetVirtualConnection.Type.POINT_TO_POINT) && (evc.niSet().size() != 2)) {
+        if (evc.type().equals(CarrierEthernetVirtualConnection.Type.POINT_TO_POINT) && (evc.uniSet().size() != 2)) {
             log.error("{} EVC requires exactly two UNIs.", evc.type().name());
             return null;
         }
@@ -253,97 +276,322 @@
         return evc;
     }
 
-    // TODO: Refactor so that EVCs are always made of FCs
     /**
      * Establish connectivity according to the EVC type (E-Line, E-Tree, E-LAN) and the EVC parameters.
      *
      * @param evc the EVC representation
-     * @return the (potentially modified) EVC that was installed or null if EVC connectivity could not be established
+     * @return the (potentially modified) EVC that was installed or null in case of failure
      */
-    public CarrierEthernetVirtualConnection establishConnectivity(CarrierEthernetVirtualConnection evc) {
+    public CarrierEthernetVirtualConnection installEvc(CarrierEthernetVirtualConnection evc) {
 
         // If EVC already exists, remove it and reestablish with new parameters
         if (evc.id() != null && evcMap.containsKey(evc.id())) {
             return updateEvc(evc);
         } else {
+            // id will be generated during validation below
             evc.setId(null);
         }
 
-        evc = validateEvc(evc);
-
-        if (evc == null) {
+        if (validateEvc(evc) == null) {
             log.error("EVC could not be installed, please check log for details.");
             return null;
         }
 
-        ceProvisioner.setupConnectivity(evc.niSet(), evc);
+        //////////////////////////////////////////////////////////////////////////////////////////////////
+        // This is the "orchestration" part of the CE app
+        //////////////////////////////////////////////////////////////////////////////////////////////////
 
-        // If no pair was connected, do not register the EVC
-        if (evc.state().equals(CarrierEthernetVirtualConnection.State.ACTIVE)) {
-            // Apply BWP-related resources (e.g. Meters) to the packet switches
-            ceProvisioner.applyBandwidthProfiles(evc);
-            // Apply the BWPs of the EVC UNI to the global UNIs, creating them if needed
-            applyBandwidthProfiles(evc.niSet());
-            // Increment the global UNI reference count
-            // FIXME: Remove this as soon as EVCs are made of FCs
-            evc.niSet().forEach(uni -> uniMap.get(uni.id()).refCount().incrementAndGet());
+        // TODO: Add configurable parameter to determine if fragmentation will take place
+        if (evcFragmentationEnabled) {
+            evc.setFcSet(fragmentEvc(evc));
+        } else {
+            evc.setFcSet(Collections.singleton(fcFromEvc(evc)));
+        }
+
+        //////////////////////////////////////////////////////////////////////////////////////////////////
+
+        // Assign VLAN ids to FCs
+        // FIXME: This was supposed to be done in the validateFc method
+        // FIXME: but we need a vlanId here already, so that S-TAGs can be assigned below among paired INNIs/ENNIs
+        // TODO: If network configuration is present, get FC vlanIds from corresponding ports
+        List<VlanId> tmpVlanIdList = new ArrayList<>();
+        evc.fcSet().forEach(fc -> {
+            fc.setVlanId(generateVlanId(tmpVlanIdList));
+            tmpVlanIdList.add(fc.vlanId());
+        });
+
+        // For each INNI/ENNI of each FC, find the paired INNI/ENNI and assign S-TAG according to the other FC's vlanId
+        for (CarrierEthernetForwardingConstruct fc : evc.fcSet()) {
+            for (CarrierEthernetLogicalTerminationPoint ltp : fc.ltpSet()) {
+                if (!ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
+                    // Find the cp at the other end of the link
+                    Link link = linkService.getEgressLinks(ltp.ni().cp()).iterator().next();
+                    String ltpId = link.dst().deviceId().toString() + "/" + link.dst().port().toString();
+                    // Find the corresponding FC - assuming LTP ids are the same as connect point ids
+                    CarrierEthernetForwardingConstruct neighborFc = getFcFromLtpId(ltpId, evc.fcSet());
+                    if (neighborFc != null) {
+                        if (ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.INNI)) {
+                            ((CarrierEthernetInni) ltp.ni()).setSVlanId(neighborFc.vlanId());
+                        } else if (ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.ENNI)) {
+                            ((CarrierEthernetEnni) ltp.ni()).setSVlanId(neighborFc.vlanId());
+                        }
+                    }
+                }
+            }
+        }
+
+        // Install the constituent FCs
+        evc.fcSet().forEach(fc -> {
+            // Increment the FC refCount
+            fc.refCount().incrementAndGet();
+            installFc(fc);
+        });
+
+        // Update the EVC UNI set based on the LTPs used during FC connectivity
+        Set<CarrierEthernetUni> usedUniSet = new HashSet<>();
+        evc.fcSet().forEach(fc -> usedUniSet.addAll(fc.uniSet()));
+        evc.setUniSet(usedUniSet);
+
+        // Determine EVC state based on the state of the constituent FCs
+        evc.setState(CarrierEthernetVirtualConnection.State.ACTIVE);
+        Iterator<CarrierEthernetForwardingConstruct> fcIt = evc.fcSet().iterator();
+        while (fcIt.hasNext()) {
+            CarrierEthernetForwardingConstruct fc = fcIt.next();
+            evc.setState(CarrierEthernetVirtualConnection.State.valueOf(fc.state().name()));
+            if (!evc.isActive()) {
+                break;
+            }
+        }
+
+        if (evc.isActive()) {
+            // If EVC installation was successful, then register the EVC
             evcMap.put(evc.id(), evc);
+        } else {
+            // If EVC installation was not successful, then do not register the EVC and rollback FC installations
+            evc.fcSet().forEach(fc -> {
+                // Decrement the FC refCount to make removal possible
+                fc.refCount().decrementAndGet();
+                removeFc(fc.id());
+            });
         }
 
         return evc;
     }
 
     /**
-     * Reestablish connectivity for an existing EVC.
+     * Creates a single FC out of an EVC.
      *
-     * @param originalEvc the updated EVC definition
-     * @return the (potentially modified) EVC that was installed or null if EVC connectivity could not be established
+     * @param evc the EVC representation
+     * @return the equivalent FC
      */
-    public CarrierEthernetVirtualConnection updateEvc(CarrierEthernetVirtualConnection originalEvc) {
-        // Just checking again
-        if (evcMap.containsKey(originalEvc.id())) {
-            log.info("Updating existing EVC {}", originalEvc.id());
-            // Keep the VLAN ID of the original EVC
-            originalEvc.setVlanId(evcMap.get(originalEvc.id()).vlanId());
-            removeEvc(originalEvc.id());
-        }
-        return establishConnectivity(originalEvc);
+    CarrierEthernetForwardingConstruct fcFromEvc(CarrierEthernetVirtualConnection evc) {
+        Set<CarrierEthernetLogicalTerminationPoint> ltpSet = new HashSet<>();
+        evc.uniSet().forEach(uni -> ltpSet.add(new CarrierEthernetLogicalTerminationPoint(null, uni)));
+        return new CarrierEthernetForwardingConstruct(null, null, evc.type(), ltpSet, null);
     }
 
     /**
-     * Applies bandwidth profiles to the UNIs of an EVC and if needed adds the UNIs to the global UNI map.
+     * Fragments an EVC into multiple FCs.
      *
-     * @param  niSet set of UNIs that are included in the EVC
+     * @param evc the EVC representation
+     * @return the set of FCs constituting the EVC
      */
-    private void applyBandwidthProfiles(Set<CarrierEthernetNetworkInterface> niSet) {
+    Set<CarrierEthernetForwardingConstruct> fragmentEvc(CarrierEthernetVirtualConnection evc) {
 
-        niSet.forEach(uni -> {
-            if (!(uniMap.keySet().contains(uni.id()))) {
-                // Just add the UNI as it appears at the EVC
-                uniMap.put(uni.id(), (CarrierEthernetUni) uni);
+        Set<CarrierEthernetForwardingConstruct> fcSet = new HashSet<>();
+
+        // Each LTP can only belong to a single FC, hence using LTP_id -> LTP_set map
+        Map<String, Set<CarrierEthernetLogicalTerminationPoint>> ltpSetMap = new HashMap<>();
+
+        // Temporary set to browse through all EVC UNI pairs
+        Set<CarrierEthernetUni> tempUniSet = new HashSet<>(evc.uniSet());
+
+        Iterator<CarrierEthernetUni> uniIt1 = tempUniSet.iterator();
+        while (uniIt1.hasNext()) {
+
+            CarrierEthernetUni uni1 = uniIt1.next();
+
+            // Iterate through all the remaining NIs
+            Iterator<CarrierEthernetUni> uniIt2 = tempUniSet.iterator();
+            while (uniIt2.hasNext()) {
+
+                CarrierEthernetUni uni2 = uniIt2.next();
+
+                // Skip equals
+                if (uni1.equals(uni2)) {
+                    continue;
+                }
+
+                // Do not establish connectivity between leaf NIs (applies to Rooted_Multipoint)
+                if (uni1.role().equals(CarrierEthernetUni.Role.LEAF)
+                        && uni2.role().equals(CarrierEthernetUni.Role.LEAF)) {
+                    continue;
+                }
+
+                // Calculate path assuming return paths are the same
+                // TODO: Handle the congruent paths case?
+                // TODO: Handle the case where uni1 and uni2 are on the same device
+                Set<Path> paths;
+                if (evc.type().equals(CarrierEthernetVirtualConnection.Type.POINT_TO_POINT)) {
+                    // For point-to-point connectivity use the pre-calculated paths
+                    // to make sure the shortest paths are chosen
+                    paths = pathService.getPaths(uni1.cp().deviceId(), uni2.cp().deviceId());
+                } else {
+                    // Recalculate path so that it's over the pre-calculated spanning tree
+                    // FIXME: Find a more efficient way (avoid recalculating paths)
+                    paths = pathService.getPaths(uni1.cp().deviceId(), uni2.cp().deviceId(),
+                            new CarrierEthernetSpanningTreeWeight(topologyService));
+                }
+
+                // Just select any of the returned paths
+                // TODO: Select path in more sophisticated way and return null if any of the constraints cannot be met
+                Path path = paths.iterator().hasNext() ? paths.iterator().next() : null;
+
+                if (path == null) {
+                    return null;
+                }
+
+                List<Link> links = new ArrayList<>();
+                links.add(createEdgeLink(uni1.cp(), true));
+                links.addAll(path.links());
+                links.add(createEdgeLink(uni2.cp(), false));
+
+                ///////////////////////////////////////////////////////////////////////////////////////////////
+                // Get LTP pairs of ingress/egress NIs along the link path (non-LTP connect points are ignored)
+                ///////////////////////////////////////////////////////////////////////////////////////////////
+
+                // Note: INNIs should always appear in pairs
+                List<Pair<CarrierEthernetLogicalTerminationPoint, CarrierEthernetLogicalTerminationPoint>> ltpPairList
+                        = new ArrayList<>();
+                CarrierEthernetLogicalTerminationPoint srcLtp = null, dstLtp = null;
+                // These are the roles that will be used for all pairs found below
+                CarrierEthernetLogicalTerminationPoint.Role srcLtpRole, dstLtpRole;
+                // The source in any pair will always have the same role as the LTP from which the paths starts
+                srcLtpRole = CarrierEthernetLogicalTerminationPoint.Role.valueOf((uni1).role().name());
+                // The destination in any pair will always have the same role as the LTP at which the path ends
+                dstLtpRole = CarrierEthernetLogicalTerminationPoint.Role.valueOf((uni2).role().name());
+                for (int i = 0 ; i < links.size() ; i++) {
+                    // Try to get the destination LTP of a pair
+                    if (srcLtp != null && i != 0) {
+                        // If this is the last, use existing EVC UNI, else create a new FC LTP and set Role
+                        dstLtp = (i == links.size() - 1) ?
+                                new CarrierEthernetLogicalTerminationPoint(null, uni2) :
+                                fcLtpFromCp(links.get(i).src(), dstLtpRole);
+                    }
+                    if (dstLtp != null) {
+                        // Create a new LTP pair and null the srcLtp so that we can continue searching for a new pair
+                        ltpPairList.add(Pair.of(srcLtp, dstLtp));
+                        srcLtp = null;
+                    }
+                    // Try to get the source LTP of a pair
+                    if (srcLtp == null && i != links.size() - 1) {
+                        // If this is the first, use existing EVC UNI, else create a new FC LTP and set Role
+                        srcLtp = (i == 0) ?
+                                new CarrierEthernetLogicalTerminationPoint(null, uni1) :
+                                fcLtpFromCp(links.get(i).dst(), srcLtpRole);
+                    }
+                }
+
+                ///////////////////////////////////////////////////////////////////////////////////////////////////////
+                // Go through all the LTP pairs found and map each LTP to a set of LTPs (create it if it doesn't exist)
+                ///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+                // Note: Each LTP can only belong to a single set, so each set will eventually correspond to an FC
+
+                ltpPairList.forEach(ltpPair -> {
+                    CarrierEthernetLogicalTerminationPoint ltp1 = ltpPair.getLeft();
+                    CarrierEthernetLogicalTerminationPoint ltp2 = ltpPair.getRight();
+                    if (ltpSetMap.containsKey(ltp1.id()) && !ltpSetMap.containsKey(ltp2.id())) {
+                        // If one of the LTPs is already contained in a set, add the other one as well in that set
+                        ltpSetMap.get(ltp1.id()).add(ltp2);
+                        ltpSetMap.put(ltp2.id(), ltpSetMap.get(ltp1.id()));
+                    } else if (ltpSetMap.containsKey(ltp2.id()) & !ltpSetMap.containsKey(ltp1.id())) {
+                        // If one of the LTPs is already contained in a set, add the other one as well in that set
+                        ltpSetMap.get(ltp2.id()).add(ltp1);
+                        ltpSetMap.put(ltp1.id(), ltpSetMap.get(ltp2.id()));
+                    } else if (!ltpSetMap.containsKey(ltp1.id()) && !ltpSetMap.containsKey(ltp2.id())){
+                        // Create a new LTP set containing the two LTPs and map both to it
+                        ltpSetMap.put(ltp1.id(), Sets.newHashSet(ltp1, ltp2));
+                        ltpSetMap.put(ltp2.id(), ltpSetMap.get(ltp1.id()));
+                    }
+                });
+            }
+            // Remove UNI from temporary set so that each pair is visited only once
+            uniIt1.remove();
+        }
+
+        //////////////////////////////////////////////////////////////////////////////////
+        // Go through all unique LTP sets generated above and create the corresponding FCs
+        //////////////////////////////////////////////////////////////////////////////////
+
+        ltpSetMap.values().stream().collect(Collectors.toSet()).forEach(ltpSet -> {
+            // Type is determined by number and type of LTPs in each set
+            CarrierEthernetVirtualConnection.Type fcType =
+                    ltpSet.size() == 2 ? CarrierEthernetVirtualConnection.Type.POINT_TO_POINT
+                            : CarrierEthernetConnection.Type.MULTIPOINT_TO_MULTIPOINT;
+            CarrierEthernetForwardingConstruct fc =
+                    new CarrierEthernetForwardingConstruct(null, null, null, ltpSet, null);
+            // If one of the LTPs is LEAF, indicate FC as ROOT_MULTIPOINT
+            for (CarrierEthernetLogicalTerminationPoint ltp : fc.ltpSet()) {
+                if (ltp.role().equals(CarrierEthernetLogicalTerminationPoint.Role.LEAF)) {
+                    fcType = CarrierEthernetConnection.Type.ROOT_MULTIPOINT;
+                    break;
+                }
+            }
+            fc.setType(fcType);
+            fcSet.add(fc);
+            log.info("Created ForwardingConstruct comprising LogicalTerminationPoints {}",
+                    ltpSet.stream().map(ltp -> ltp.id()).collect(Collectors.toList()));
+        });
+
+        return fcSet;
+    }
+
+    /**
+     * Reestablish connectivity for an existing EVC.
+     *
+     * @param evc the updated EVC definition
+     * @return the (potentially modified) EVC that was installed or null if EVC connectivity could not be established
+     */
+    public CarrierEthernetVirtualConnection updateEvc(CarrierEthernetVirtualConnection evc) {
+        // Just checking again
+        if (evcMap.containsKey(evc.id())) {
+            log.info("Updating existing EVC {}", evc.id());
+            removeEvc(evc.id());
+        }
+        return installEvc(evc);
+    }
+
+    /**
+     * Applies FC- specific LTP attributes to global LTPs or adds them to the global LTP map if not there.
+     *
+     * @param ltpSet set of FC-specific LTPs the attributes of which will be applied to the global LTPs
+     */
+    private void applyFcToGlobalLtps(Set<CarrierEthernetLogicalTerminationPoint> ltpSet) {
+        ltpSet.forEach(ltp -> {
+            if (!(ltpMap.keySet().contains(ltp.id()))) {
+                // Just add the LTP as it appears at the FC
+                addGlobalLtp(ltp);
             } else {
-                // Add UNI resources (BWP, CE-VLAN ID) to existing global UNI
-                uniMap.get(uni.id()).addEvcUni((CarrierEthernetUni) uni);
+                // Add LTP resources (BWP, CE-VLAN ID, S-TAG) to existing global LTP
+                ltpMap.get(ltp.id()).ni().addEcNi(ltp.ni());
                 // Update config identifier
-                uniMap.get(uni.id()).setCfgId(uni.cfgId());
+                ltpMap.get(ltp.id()).ni().setCfgId(ltp.ni().cfgId());
             }
         });
     }
 
     /**
-     * Removes bandwidth profiles from the UNIs of an EVC and removes them if needed from the global UNI map.
+     * Removes bandwidth profiles from the UNIs of an FC.
      *
-     * @param evcId the EVC id
+     * @param fc the FC representation
      */
-    private void removeBandwidthProfiles(String evcId) {
-
-        evcMap.get(evcId).niSet().forEach(uni -> {
-            // TODO: Check if the bandwidth profile really needs to be removed (e.g. may be CoS)
-            ceProvisioner.removeBandwidthProfiles(evcMap.get(evcId));
-
-            // Remove UNI resources (BWP, CE-VLAN ID) from global UNI
-            uniMap.get(uni.id()).removeEvcUni((CarrierEthernetUni) uni);
-        });
+    // TODO: Remove LTPs if needed from the global LTP/UNI map
+    private void removeFcFromGlobalLtps(CarrierEthernetForwardingConstruct fc) {
+        // TODO: Check if the bandwidth profile really needs to be removed (e.g. may be CoS)
+        ceProvisioner.removeBandwidthProfiles(fc);
+        // Remove LTP resources (BWP, CE-VLAN ID, S-TAG) from corresponding global LTPs
+        fc.ltpSet().forEach(ltp -> ltpMap.get(ltp.id()).ni().removeEcNi(ltp.ni()));
     }
 
     /**
@@ -352,18 +600,7 @@
      * This will be called either from the deactivate method or as a response to a CLI/REST command.
      * */
     public void removeAllEvcs() {
-        evcMap.keySet().forEach(evcId -> {
-            CarrierEthernetVirtualConnection evc = evcMap.get(evcId);
-            ceProvisioner.removeConnectivity(evc);
-            ceProvisioner.removeBandwidthProfiles(evc);
-            removeBandwidthProfiles(evcId);
-            // Avoid excessively incrementing VLAN ids
-            nextVlanId = (evc.vlanId().toShort() < nextVlanId ? evc.vlanId().toShort() : nextVlanId);
-            // Decrement the global UNI and corresponding NI refCount
-            // FIXME: Remove this as soon as EVCs are always made of FCs
-            evc.niSet().forEach(uni -> uniMap.get(uni.id()).refCount().decrementAndGet());
-        });
-        evcMap.clear();
+        evcMap.keySet().forEach(evcId -> removeEvc(evcId));
     }
 
     /**
@@ -374,14 +611,13 @@
     public void removeEvc(String evcId) {
         if (evcMap.containsKey(evcId)) {
             CarrierEthernetVirtualConnection evc = evcMap.get(evcId);
-            ceProvisioner.removeConnectivity(evc);
-            ceProvisioner.removeBandwidthProfiles(evc);
-            removeBandwidthProfiles(evcId);
-            // Avoid excessively incrementing VLAN ids
-            nextVlanId = (evc.vlanId().toShort() < nextVlanId ? evc.vlanId().toShort() : nextVlanId);
-            // Decrement the global UNI and corresponding NI refCount
-            // FIXME: Remove this as soon as EVCs are always made of FCs
-            evc.niSet().forEach(uni -> uniMap.get(uni.id()).refCount().decrementAndGet());
+            evc.fcSet().forEach(fc -> {
+                // Decrement the FC refCount to make removal possible
+                fc.refCount().decrementAndGet();
+                removeFc(fc.id());
+            });
+            // Avoid excessively incrementing EVC ids
+            nextEvcShortId = evc.shortId() < nextEvcShortId ? evc.shortId() : nextEvcShortId;
             evcMap.remove(evcId);
         }
     }
@@ -396,76 +632,56 @@
 
         // Try to set a unique VLAN id for the FC unless the EVC is being updated
         // TODO: Add different connectivity types
-        fc.evcLite().setVlanId(generateVlanId());
-        if (fc.evcLite().vlanId() == null) {
+        // FIXME: This is an extra check to be able to generate/set VLAN id for FC before calling installFc
+        if (fc.vlanId() == null) {
+            fc.setVlanId(generateVlanId(null));
+        }
+        if (fc.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 virtualEvc flag accordingly
-        boolean isVirtual = false;
-        Iterator<CarrierEthernetNetworkInterface> it = fc.evcLite().niSet().iterator();
-        while (it.hasNext()) {
-            CarrierEthernetUni uni = (CarrierEthernetUni) it.next();
-            if (uni.ceVlanId() == null && isVirtual) {
-                log.error("Could not validate the virtual status of the EVC.");
-                return null;
-            } else if (uni.ceVlanId() != null){
-                isVirtual = true;
-            }
-        }
-        fc.evcLite().setIsVirtual(isVirtual);
-
         // Generate and set unique FC id
-        fc.setId(generateEvcId(fc.evcLite()));
-        fc.evcLite().setId(fc.id());
+        fc.setId(generateFcId(fc));
 
-        Set<CarrierEthernetUni> validatedUniSet = new HashSet<>();
-
-        // Check the UNIs of the EVC, possibly removing UNIs that are incompatible with existing ones
-        it = fc.evcLite().niSet().iterator();
-        while (it.hasNext()) {
-            CarrierEthernetUni uni = (CarrierEthernetUni) it.next();
-            // Change the name of the UNI's BWP to the EVC name if it is an EVC BWP
-            if (uni.bwp().type().equals(CarrierEthernetBandwidthProfile.Type.EVC)) {
-                uni.bwp().setId(fc.evcLite().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 EVC-specific UNI is compatible with the global one
-                if (!(existingUni.validateEvcUni(uni))) {
-                    // If EVC is of ROOT_MULTIPOINT type and we have removed the root, return null
-                    if (fc.evcLite().type() == CarrierEthernetVirtualConnection.Type.ROOT_MULTIPOINT &&
-                            uni.role() == CarrierEthernetUni.Role.ROOT) {
-                        log.error("Root UNI could not be added to %s EVC.", fc.evcLite().type().name());
-                        return null;
-                    }
-                    log.warn("UNI {} could not be added to EVC.", uni.id());
-                    continue;
-                } else {
-                    // Add UNI to evc description
-                    validatedUniSet.add(uni);
-                }
-            } else {
-                // Add UNI to EVC description
-                validatedUniSet.add(uni);
-            }
-        }
-
-        // TODO: Add validation for INNIs/ENNIs as well
-
-        // Update the FC LTP set, based on the UNIs actually used
         Set<CarrierEthernetLogicalTerminationPoint> validatedLtpSet = new HashSet<>();
+
+        // Check the NIs of the FC, possibly removing NIs that are incompatible with existing ones
         Iterator<CarrierEthernetLogicalTerminationPoint> ltpIt = fc.ltpSet().iterator();
-        while(ltpIt.hasNext()) {
+        while (ltpIt.hasNext()) {
             CarrierEthernetLogicalTerminationPoint ltp = ltpIt.next();
-            if ((ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI))
-                    && (!validatedUniSet.contains(ltp.ni()))) {
-                continue;
+            boolean ltpValidated = true;
+            if (ltp.type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
+                CarrierEthernetUni uni = (CarrierEthernetUni) ltp.ni();
+                // Change the name of the UNI's BWP to the FC name if it is an EVC BWP
+                if (uni.bwp().type().equals(CarrierEthernetBandwidthProfile.Type.EVC)) {
+                    // FIXME: Find a way to use the EVC name instead
+                    uni.bwp().setId(fc.id());
+                }
             }
-            validatedLtpSet.add(ltp);
+            // Check first if LTP already exists by checking against the global LTP Map
+            if (ltpMap.keySet().contains(ltp.id())) {
+                CarrierEthernetNetworkInterface existingNi = ltpMap.get(ltp.id()).ni();
+                // Check if the FC-specific NI is compatible with the global one
+                if (!(existingNi.validateEcNi(ltp.ni()))) {
+                    ltpValidated = false;
+                }
+            }
+            if (!ltpValidated) {
+                // If EVC is of ROOT_MULTIPOINT type and we have removed the root, return null
+                if (fc.type() == CarrierEthernetForwardingConstruct.Type.ROOT_MULTIPOINT &&
+                        ltp.role() == CarrierEthernetLogicalTerminationPoint.Role.ROOT) {
+                    log.error("Root LTP could not be added to %s FC.", fc.type().name());
+                    return null;
+                }
+                log.warn("LTP {} could not be added to FC.", ltp.id());
+                continue;
+            } else {
+                // Add LTP to FC description
+                validatedLtpSet.add(ltp);
+            }
         }
+
         fc.setLtpSet(validatedLtpSet);
 
         return fc;
@@ -486,55 +702,23 @@
             fc.setId(null);
         }
 
-        fc = validateFc(fc);
-
-        if (fc == null) {
+        if (validateFc(fc) == null) {
             log.error("FC could not be installed, please check log for details.");
             return null;
         }
 
-        // Temporary set for iterating through FC NI pairs
-        Set<CarrierEthernetNetworkInterface> niSet = new HashSet<>();
-        fc.ltpSet().forEach(ltp -> {
-            niSet.add(ltp.ni());
-        });
+        ceProvisioner.setupConnectivity(fc);
 
-        ceProvisioner.setupConnectivity(niSet, fc.evcLite());
-
-        // FIXME: Update the LTP set within setupConnectivity()
-        // Update the FC LTP set, based on the NIs actually used
-        Set<CarrierEthernetLogicalTerminationPoint> usedLtpSet = new HashSet<>();
-        Iterator<CarrierEthernetLogicalTerminationPoint> ltpIt = fc.ltpSet().iterator();
-        while (ltpIt.hasNext()) {
-            CarrierEthernetLogicalTerminationPoint ltp = ltpIt.next();
-            if (fc.evcLite().niSet().contains(ltp.ni())) {
-                usedLtpSet.add(ltp);
-            }
-        }
-        fc.setLtpSet(usedLtpSet);
-
-        // Set FC state according to EVC Lite state
-        fc.setState(CarrierEthernetForwardingConstruct.State.valueOf(fc.evcLite().state().name()));
-        fc.setActiveState(CarrierEthernetForwardingConstruct.ActiveState.valueOf(fc.evcLite().activeState().name()));
-
-        // If no pair was connected, do not register the FC
+        // If connectivity was not successful, then do not register the FC and do not apply BW profiles
         if (fc.state().equals(CarrierEthernetForwardingConstruct.State.ACTIVE)) {
             // Apply BWP-related resources (e.g. Meters) to the packet switches
-            // FIXME: Hack - Resetting the evcLite set to only UNIs until setupConnectivity() uses FCs
-            Set<CarrierEthernetNetworkInterface> tempNiSet = new HashSet<>();
-            fc.ltpSet().forEach(ltp -> {
-                if (ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
-                    tempNiSet.add(ltp.ni());
-                }
-            });
-            fc.evcLite().setNiSet(tempNiSet);
-            ceProvisioner.applyBandwidthProfiles(fc.evcLite());
-            // Apply the BWPs of the EVC UNI to the global UNIs, creating them if needed
-            applyBandwidthProfiles(fc.evcLite().niSet());
+            ceProvisioner.applyBandwidthProfiles(fc);
+            // Apply the BWPs of the FC UNIs to the global UNIs, creating them if needed
+            //applyEvcToGlobalUnis(fc.uniSet());
+            applyFcToGlobalLtps(fc.ltpSet());
             // Increment the global LTP and corresponding NI refCount
             fc.ltpSet().forEach(ltp -> ltpMap.get(ltp.id()).refCount().incrementAndGet());
             fcMap.put(fc.id(), fc);
-            evcMap.put(fc.evcLite().id(), fc.evcLite());
         }
 
         return fc;
@@ -551,7 +735,8 @@
         if (fcMap.containsKey(fc.id())) {
             log.info("Updating existing FC {}", fc.id());
             // Keep the VLAN ID of the original FC
-            fc.evcLite().setVlanId(fcMap.get(fc.id()).evcLite().vlanId());
+            fc.setVlanId(fcMap.get(fc.id()).vlanId());
+            // FIXME: Currently FC update only possible for standalone FCs
             removeFc(fc.id());
         }
         return installFc(fc);
@@ -563,49 +748,49 @@
      * This will be called either from the deactivate method or as a response to a CLI command.
      * */
     public void removeAllFcs() {
-        fcMap.keySet().forEach(fcId -> {
-            removeEvc(fcMap.get(fcId).evcLite().id());
-            // FIXME: Remove the UNI constraint as soon as EVCs are always made of FCs
-            fcMap.get(fcId).ltpSet()
-                    .forEach(ltp -> {
-                        if (!(ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI))) {
-                            ltpMap.get(ltp.id()).refCount().decrementAndGet();
-                        }
-                    });
-        });
-        fcMap.clear();
+        fcMap.keySet().forEach(fcId -> removeFc(fcId));
     }
 
     /**
      * Removes all resources associated with a specific FC.
      *
      * @param fcId the FC id
+     * @return the FC that was removed or null if removal failed
      * */
-    public void removeFc(String fcId) {
+    public CarrierEthernetForwardingConstruct removeFc(String fcId) {
         if (fcMap.containsKey(fcId)) {
-            // FIXME: For now, UNI refCount will be updated in removeEvc()
-            removeEvc(fcMap.get(fcId).evcLite().id());
+            CarrierEthernetForwardingConstruct fc = fcMap.get(fcId);
+            if (fc.refCount().get() != 0) {
+                log.warn("Could not remove FC {}: RefCount is not zero", fc.id());
+                return null;
+            }
+            ceProvisioner.removeConnectivity(fc);
+            ceProvisioner.removeBandwidthProfiles(fc);
+            removeFcFromGlobalLtps(fc);
+            // Avoid excessively incrementing FC VLAN ids
+            nextVlanId = (fcMap.get(fcId).vlanId().toShort() < nextVlanId ? fcMap.get(fcId).vlanId().toShort() : nextVlanId);
             // Decrement the global LTP and corresponding NI refCount
-            // FIXME: Remove the UNI constraint as soon as EVCs are always constructed of FCs
-            fcMap.get(fcId).ltpSet()
-                    .forEach(ltp -> {
-                        if (!(ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI))) {
-                            ltpMap.get(ltp.id()).refCount().decrementAndGet();
-                        }
-                    });
+            fcMap.get(fcId).ltpSet().forEach(ltp -> ltpMap.get(ltp.id()).refCount().decrementAndGet());
             fcMap.remove(fcId);
+            return fc;
         }
+        return null;
     }
 
     /**
      * Generates a unique vlanId in the context of the CE app.
      *
+     * @param extraVlanList additional vlanIds to be excluded from the vlanId generation
      * @return the generated vlanId or null if none found
      * */
-    public VlanId generateVlanId() {
+    // FIXME: Providing extra VLAN list as argument is probably a temporary solution
+    public VlanId generateVlanId(List<VlanId> extraVlanList) {
 
-        List<VlanId> vlanList = evcMap.values().stream().map(CarrierEthernetVirtualConnection::vlanId)
-                .collect(Collectors.toList());
+        List<VlanId> vlanList = fcMap.values().stream().map(CarrierEthernetForwardingConstruct::vlanId)
+                        .collect(Collectors.toList());
+        if (extraVlanList != null) {
+            vlanList.addAll(extraVlanList);
+        }
 
         // If all vlanIds are being used return null, else try to find the next available one
         if (vlanList.size() <  VlanId.MAX_VLAN - 1) {
@@ -619,12 +804,36 @@
     }
 
     /**
+     * Generates a unique vlanId in the context of the CE app.
+     *
+     * @return the generated vlanId or null if none found
+     * */
+    private Short generateEvcShortId() {
+
+        List<Short> evcShortIdList = evcMap.values()
+                .stream()
+                .map(evc -> Short.valueOf(evc.shortId()))
+                .collect(Collectors.toList());
+
+        // If all vlanIds are being used return null, else try to find the next available one
+        if (evcShortIdList.size() <  Short.MAX_VALUE - 1) {
+            while (evcShortIdList.contains(nextEvcShortId)) {
+                // Get next valid short
+                nextEvcShortId =
+                        (nextEvcShortId >= Short.MAX_VALUE || nextEvcShortId <= 0 ? 1 : (short) (nextEvcShortId + 1));
+            }
+        }
+
+        return evcShortIdList.contains(nextEvcShortId) ? null : nextEvcShortId;
+    }
+
+    /**
      * Generates a unique EVC id in the context of the CE app.
      *
      * @param evc the EVC representation
      * @return the generated EVC id or null if none found
      * */
-    public String generateEvcId(CarrierEthernetVirtualConnection evc) {
+    private String generateEvcId(CarrierEthernetVirtualConnection evc) {
 
         // TODO: Add different connectivity types
 
@@ -638,19 +847,31 @@
             tmpType = "Tree";
         }
 
-        String evcId = "E" + (evc.isVirtual() ? "V" : "") + "P-" + tmpType + "-" +
-                evc.vlanId().toString();
-
-        return evcId;
+        return "E" + (evc.isVirtual() ? "V" : "") + "P-" + tmpType + "-" +
+                evc.shortId().toString();
     }
 
     /**
-     * Remove an LTP from the set of global LTPs.
+     * Generates a unique FC id in the context of the CE app.
+     *
+     * @param fc the FC representation
+     * @return the generated FC id or null if none found
+     * */
+    private String generateFcId(CarrierEthernetForwardingConstruct fc) {
+
+        // TODO: Add different connectivity types
+
+        return "FC-" + fc.vlanId().toString();
+    }
+
+    /**
+     * Remove an LTP from the set of global LTPs, as well as the corresponding INNI LTP at the other end of the link.
      *
      *
      * @param ltpId the id of the LTP to be removed
      * @return the LTP that was removed or null in case of failure (didn't exist of refCount was not 0)
      * */
+    // TODO: Add removeAllGlobalLtps method (or command only?)
     public CarrierEthernetLogicalTerminationPoint removeGlobalLtp(String ltpId) {
 
         if (!ltpMap.containsKey(ltpId)) {
@@ -665,12 +886,23 @@
 
         // Remove LTP from ltpMap and (if needed) UNI from uniMap
         CarrierEthernetLogicalTerminationPoint ltp = ltpMap.remove(ltpId);
-        if (ltp.ni() instanceof CarrierEthernetUni) {
-            removeGlobalUni(ltp.ni().id());
-        }
-
         // Add LTP to removed set
         removedLtpSet.add(ltpId);
+        if (ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
+            removeGlobalUni(ltp.ni().id());
+            // Add UNI to removed set
+            // TODO: Check if this is right
+            removedUniSet.add(ltp.ni().id());
+        }
+
+        // Find cp at other end of link and try to remove both LTPs - assuming LTP ids are the same as connect point ids
+        if (ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.INNI)) {
+            Link link = linkService.getEgressLinks(ltp.ni().cp()).iterator().next();
+            String pairLtpId = link.dst().deviceId().toString() + "/" + link.dst().port().toString();
+            ltpMap.remove(pairLtpId);
+            // Add LTP to removed set
+            removedLtpSet.add(pairLtpId);
+        }
 
         return ltp;
     }
@@ -681,6 +913,7 @@
      * @param uniId the id of the UNI to be removed
      * @return the UNI that was removed or null in case of failure (didn't exist of refCount was not 0)
      * */
+    // TODO: Add removeAllGlobalUnis method (or command only?)
     public CarrierEthernetUni removeGlobalUni(String uniId) {
 
         if (!uniMap.containsKey(uniId)) {
@@ -698,7 +931,7 @@
         // Note: If refCount for UNI is not zero, then it should be for the corresponding LTP as well
         ltpMap.remove(uniId);
 
-        // Add UNI to removed set
+        // Add UNI and LTP to removed set
         removedUniSet.add(uniId);
         removedLtpSet.add(uniId);
 
@@ -865,7 +1098,7 @@
             return null;
         }
 
-        return new CarrierEthernetLogicalTerminationPoint(cp, ltpId, ltpType);
+        return new CarrierEthernetLogicalTerminationPoint(cp, ltpId, ltpType, null);
     }
 
     /**
@@ -908,14 +1141,14 @@
     }
 
     /**
-     * Adds a potential LTP and its UNI to the global LTP/UNI maps if it's not already there.
+     * Adds a potential LTP and its UNI or pair INNI to the global LTP/UNI maps if they are not already there.
      *
      * @param ltp the potential LTP to add to global LTP map
      * @return the LTP that was added or null if it already existed
      * */
     public CarrierEthernetLogicalTerminationPoint addGlobalLtp(CarrierEthernetLogicalTerminationPoint ltp) {
         // If LTP contains a UNI, add it only if it's not already there, else point to the existing UNI
-        if (ltp.ni() != null && ltp.ni() instanceof CarrierEthernetUni) {
+        if (ltp.ni() != null && ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
             if (!uniMap.containsKey(ltp.ni().id())) {
                 uniMap.put(ltp.ni().id(), (CarrierEthernetUni) ltp.ni());
             } else {
@@ -924,6 +1157,20 @@
         }
         // Add LTP only if it's not already there
         if (!ltpMap.containsKey(ltp.id())) {
+            // Try to create and add INNI LTP at other end of link as well
+            if (ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.INNI)) {
+                Link link = linkService.getEgressLinks(ltp.ni().cp()).iterator().next();
+                CarrierEthernetLogicalTerminationPoint pairLtp =
+                        generateLtp(link.dst(), CarrierEthernetNetworkInterface.Type.INNI);
+                if (pairLtp == null) {
+                    return null;
+                }
+                if (!ltpMap.containsKey(pairLtp.id())) {
+                    ltpMap.put(pairLtp.id(), pairLtp);
+                } else {
+                    return null;
+                }
+            }
             ltpMap.put(ltp.id(), ltp);
             return ltp;
         } else {
@@ -931,4 +1178,45 @@
         }
     }
 
+    /**
+     * Utility method to obtain an FC-specific LTP (UNI/INNI or ENNI) associated with a connect point.
+     *
+     * @param cp the connect point to check
+     * @return a new FC-specific LTP associated with cp if the corresponding global LTP exists or null otherwise
+     * */
+    private CarrierEthernetLogicalTerminationPoint fcLtpFromCp(ConnectPoint cp,
+                                                               CarrierEthernetLogicalTerminationPoint.Role ltpRole) {
+        // Check first if cp is associated with a device
+        if (cp.deviceId() == null) {
+            return null;
+        }
+        // Assuming LTP id is the same as the connect point id
+        String cpId = cp.deviceId().toString() + "/" + cp.port().toString();
+        if (ltpMap.containsKey(cpId)) {
+            CarrierEthernetLogicalTerminationPoint ltp =
+                    new CarrierEthernetLogicalTerminationPoint(cp, cpId, ltpMap.get(cpId).type(), ltpRole);
+            return ltp;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Utility method to obtain the first FC in a set which contains the LTP with the provided id.
+     *
+     * @param ltpId the LTP id to search
+     * @param fcSet the FC set to search
+     * @return a the first FC found in fcSet which contains an LTP with id ltpId, or null if no such FC is found
+     * */
+    // FIXME: Find more efficient way to do that
+    private CarrierEthernetForwardingConstruct getFcFromLtpId(String ltpId,
+                                                              Set<CarrierEthernetForwardingConstruct> fcSet) {
+        // Get the first FC that contains the LTP with the provided id
+        for (CarrierEthernetForwardingConstruct fc : fcSet) {
+            if (!fc.ltpSet().stream().filter(ltp -> ltp.id().equals(ltpId)).collect(Collectors.toList()).isEmpty()) {
+                return fc;
+            }
+        }
+        return null;
+    }
 }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetNetworkInterface.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetNetworkInterface.java
index 4c0e3b4..f77cd62 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetNetworkInterface.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetNetworkInterface.java
@@ -30,7 +30,7 @@
 /**
  * Representation of a Carrier Ethernet Network Interface (UNI, INNI or ENNI).
  */
-public abstract class CarrierEthernetNetworkInterface {
+public abstract class CarrierEthernetNetworkInterface <E extends CarrierEthernetNetworkInterface> {
 
     protected DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
 
@@ -187,6 +187,28 @@
      */
     public abstract <T> T role();
 
+    /**
+     * Adds the resources associated with an EVC- or FC-specific NI to a global NI.
+     *
+     * @param ni the EVC- or FC-specific NI to be added
+     */
+    public abstract void addEcNi(E ni);
+
+    /**
+     * Removes the resources associated with an EVC- or FC-specific NI from a global NI.
+     *
+     * @param ni the EVC- or FC-specific NI to be removed
+     */
+    public abstract void removeEcNi(E ni);
+
+    /**
+     * Validates whether an EVC- or FC-specific NI is compatible with the corresponding global NI.
+     *
+     * @param ni the EVC- or FC-specific NI
+     * @return boolean value indicating whether the NIs are compatible
+     */
+    public abstract boolean validateEcNi(E ni);
+
     @Override
     public String toString() {
 
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
index 10bc43b..285fe5f 100644
--- 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
@@ -96,7 +96,7 @@
     protected void deactivate() {}
 
     @Override
-    public void setNodeForwarding(CarrierEthernetVirtualConnection evc, CarrierEthernetNetworkInterface ingressNi,
+    public void setNodeForwarding(CarrierEthernetForwardingConstruct fc, CarrierEthernetNetworkInterface ingressNi,
                                   Set<CarrierEthernetNetworkInterface> egressNiSet) {
 
         if (ingressNi == null || egressNiSet.isEmpty()) {
@@ -104,20 +104,20 @@
             return;
         }
 
-        flowObjectiveMap.putIfAbsent(evc.id(), new LinkedList<>());
+        flowObjectiveMap.putIfAbsent(fc.id(), new LinkedList<>());
 
         // TODO: Get created FlowObjectives from this method
-        createFlowObjectives(evc, ingressNi, egressNiSet);
+        createFlowObjectives(fc, ingressNi, egressNiSet);
     }
 
     /**
-     * Creates and submits FlowObjectives depending on role of the device in the EVC and ingress/egress NI types.
+     * Creates and submits FlowObjectives depending on role of the device in the FC and ingress/egress NI types.
      *
-     * @param evc the EVC representation
+     * @param fc the FC representation
      * @param ingressNi the ingress NI (UNI, INNI, ENNI or GENERIC) of the EVC for this forwarding segment
      * @param  egressNiSet the set of egress NIs (UNI, INNI, ENNI or GENERIC) of the EVC for this forwarding segment
      */
-    private void createFlowObjectives(CarrierEthernetVirtualConnection evc, CarrierEthernetNetworkInterface ingressNi,
+    private void createFlowObjectives(CarrierEthernetForwardingConstruct fc, CarrierEthernetNetworkInterface ingressNi,
                                       Set<CarrierEthernetNetworkInterface> egressNiSet) {
 
         /////////////////////////////////////////
@@ -132,7 +132,7 @@
         TrafficTreatment.Builder filterTreatmentBuilder = DefaultTrafficTreatment.builder();
 
         // In general, nodes would match on the VLAN tag assigned to the EVC/FC
-        Criterion filterVlanIdCriterion = Criteria.matchVlanId(evc.vlanId());
+        Criterion filterVlanIdCriterion = Criteria.matchVlanId(fc.vlanId());
 
         if ((ingressNi.type().equals(CarrierEthernetNetworkInterface.Type.INNI))
                 || (ingressNi.type().equals(CarrierEthernetNetworkInterface.Type.ENNI)) ) {
@@ -140,12 +140,12 @@
             // Source node of an FC should match on S-TAG if it's an INNI/ENNI
             filterVlanIdCriterion = Criteria.matchVlanId(ingressNi.sVlanId());
             // Translate S-TAG to the one used in the current FC
-            filterTreatmentBuilder.setVlanId(evc.vlanId());
+            filterTreatmentBuilder.setVlanId(fc.vlanId());
         } else if (ingressNi.type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
             // Source node of an FC should match on CE-VLAN ID (if present) if it's a UNI
             filterVlanIdCriterion = Criteria.matchVlanId(ingressNi.ceVlanId());
             // Push S-TAG of current FC on top of existing CE-VLAN ID
-            filterTreatmentBuilder.pushVlan().setVlanId(evc.vlanId());
+            filterTreatmentBuilder.pushVlan().setVlanId(fc.vlanId());
         }
 
         filteringObjectiveBuilder.addCondition(filterVlanIdCriterion);
@@ -156,14 +156,14 @@
         }
 
         flowObjectiveService.filter(ingressNi.cp().deviceId(), filteringObjectiveBuilder.add());
-        flowObjectiveMap.get(evc.id()).addFirst(Pair.of(ingressNi.cp().deviceId(), filteringObjectiveBuilder.add()));
+        flowObjectiveMap.get(fc.id()).addFirst(Pair.of(ingressNi.cp().deviceId(), filteringObjectiveBuilder.add()));
 
         ////////////////////////////////////////////////////
         // Prepare and submit next and forwarding objectives
         ////////////////////////////////////////////////////
 
         TrafficSelector fwdSelector = DefaultTrafficSelector.builder()
-                .matchVlanId(evc.vlanId())
+                .matchVlanId(fc.vlanId())
                 .matchInPort(ingressNi.cp().port())
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .build();
@@ -205,14 +205,14 @@
 
         flowObjectiveService.next(ingressNi.cp().deviceId(), nextObjective);
         // Add all NextObjectives at the end of the list so that they will be removed last
-        flowObjectiveMap.get(evc.id()).addLast(Pair.of(ingressNi.cp().deviceId(), nextObjective));
+        flowObjectiveMap.get(fc.id()).addLast(Pair.of(ingressNi.cp().deviceId(), nextObjective));
 
         flowObjectiveService.forward(ingressNi.cp().deviceId(), forwardingObjective);
-        flowObjectiveMap.get(evc.id()).addFirst(Pair.of(ingressNi.cp().deviceId(), forwardingObjective));
+        flowObjectiveMap.get(fc.id()).addFirst(Pair.of(ingressNi.cp().deviceId(), forwardingObjective));
     }
 
     @Override
-    void applyBandwidthProfileResources(CarrierEthernetVirtualConnection evc, CarrierEthernetUni uni) {
+    void applyBandwidthProfileResources(CarrierEthernetForwardingConstruct fc, CarrierEthernetUni uni) {
 
         Dpid dpid = Dpid.dpid(uni.cp().deviceId().uri());
         OpenFlowSwitch sw = controller.getSwitch(dpid);
@@ -223,12 +223,12 @@
         }
 
         // Create meters and add them to global MeterId map
-        Set<DeviceMeterId> deviceMeterIdSet = deviceMeterIdMap.get(evc.id());
+        Set<DeviceMeterId> deviceMeterIdSet = deviceMeterIdMap.get(fc.id());
         if (deviceMeterIdSet == null) {
             deviceMeterIdSet = new HashSet<>();
         }
         deviceMeterIdSet.addAll(createMeters(uni));
-        deviceMeterIdMap.put(evc.id(), deviceMeterIdSet);
+        deviceMeterIdMap.put(fc.id(), deviceMeterIdSet);
 
         // Apply meters to already installed flows
 
@@ -251,10 +251,11 @@
             }
             ConnectPoint flowInCp = new ConnectPoint(flowRule.deviceId(), inPort);
             // FIXME: Maybe check also if there is a group action?
-            if (uni.cp().equals(flowInCp) && evc.vlanId().equals(flowInVlanId)) {
+            // TODO: Check the vlanIds of all FCs comprising the EVC????
+            if (uni.cp().equals(flowInCp) && fc.vlanId().equals(flowInVlanId)) {
                 // Need to add to the flow the meters associated with the same device
                 Set<DeviceMeterId> tmpDeviceMeterIdSet = new HashSet<>();
-                deviceMeterIdMap.get(evc.id()).forEach(deviceMeterId -> {
+                deviceMeterIdMap.get(fc.id()).forEach(deviceMeterId -> {
                     if (deviceMeterId.deviceId().equals(flowRule.deviceId())) {
                         tmpDeviceMeterIdSet.add(deviceMeterId);
                     }
@@ -359,20 +360,19 @@
     }
 
     @Override
-    void removeBandwidthProfileResources(String serviceId, CarrierEthernetUni uni) {
-
-        removeMeters(serviceId, uni);
+    void removeBandwidthProfileResources(String fcId, CarrierEthernetUni uni) {
+        removeMeters(fcId, uni);
     }
 
     /**
-     * Removes the meters associated with a specific UNI of a service.
+     * Removes the meters associated with a specific UNI of an FC.
      *
-     * @param serviceId the CE service ID
+     * @param fcId the EVC ID
      * @param uni the UNI descriptor
      * */
-    private void removeMeters(String serviceId, CarrierEthernetUni uni) {
+    private void removeMeters(String fcId, CarrierEthernetUni uni) {
 
-        Set<DeviceMeterId> newDeviceMeterIdSet = deviceMeterIdMap.get(serviceId);
+        Set<DeviceMeterId> newDeviceMeterIdSet = deviceMeterIdMap.get(fcId);
         DeviceMeterId tmpDeviceMeterId;
 
         Collection<Meter> meters = meterService.getMeters(uni.cp().deviceId());
@@ -382,7 +382,7 @@
             Meter meter = it.next();
             tmpDeviceMeterId = new DeviceMeterId(uni.cp().deviceId(), meter.id());
             if (meter.appId().equals(appId) &&
-                    deviceMeterIdMap.get(serviceId).contains(tmpDeviceMeterId)) {
+                    deviceMeterIdMap.get(fcId).contains(tmpDeviceMeterId)) {
                 MeterRequest.Builder mBuilder;
                 mBuilder = DefaultMeterRequest.builder()
                         .fromApp(meter.appId())
@@ -397,22 +397,22 @@
             }
         }
 
-        deviceMeterIdMap.put(serviceId, newDeviceMeterIdSet);
+        deviceMeterIdMap.put(fcId, newDeviceMeterIdSet);
     }
 
     @Override
-    void removeAllForwardingResources(CarrierEthernetVirtualConnection evc) {
-        removeFlowObjectives(evc.id());
+    void removeAllForwardingResources(CarrierEthernetForwardingConstruct fc) {
+        removeFlowObjectives(fc.id());
     }
 
     /**
-     * Removes all flow objectives installed by the application which are associated with a specific EVC.
+     * Removes all flow objectives installed by the application which are associated with a specific FC.
      *
-     * @param evcId the EVC id
+     * @param fcId the FC id
      * */
-    private void removeFlowObjectives(String evcId) {
-        // Note: A Flow Rule cannot be shared by multiple services due to different VLAN or CE-VLAN ID match.
-        List<Pair<DeviceId, Objective>> flowObjectiveList = flowObjectiveMap.remove(evcId);
+    private void removeFlowObjectives(String fcId) {
+        // Note: A Flow Rule cannot be shared by multiple FCs due to different VLAN or CE-VLAN ID match.
+        List<Pair<DeviceId, Objective>> flowObjectiveList = flowObjectiveMap.remove(fcId);
         // NextObjectives will be removed after all other Objectives
         ListIterator<Pair<DeviceId, Objective>> objIter = flowObjectiveList.listIterator();
         while (objIter.hasNext()) {
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
index 4be8901..b34d12a 100644
--- 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
@@ -22,13 +22,13 @@
  */
 public abstract class CarrierEthernetPacketNodeManager {
 
-    abstract void setNodeForwarding(CarrierEthernetVirtualConnection evc, CarrierEthernetNetworkInterface srcNi,
+    abstract void setNodeForwarding(CarrierEthernetForwardingConstruct fc, CarrierEthernetNetworkInterface srcNi,
                                     Set<CarrierEthernetNetworkInterface> dstNiSet);
 
-    abstract void applyBandwidthProfileResources(CarrierEthernetVirtualConnection service, CarrierEthernetUni uni);
+    abstract void applyBandwidthProfileResources(CarrierEthernetForwardingConstruct fc, CarrierEthernetUni uni);
 
-    abstract void removeBandwidthProfileResources(String serviceId, CarrierEthernetUni uni);
+    abstract void removeBandwidthProfileResources(String fcId, CarrierEthernetUni uni);
 
-    abstract void removeAllForwardingResources(CarrierEthernetVirtualConnection service);
+    abstract void removeAllForwardingResources(CarrierEthernetForwardingConstruct fc);
 
 }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetProvisioner.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetProvisioner.java
index e60f08d..bbbecad 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetProvisioner.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetProvisioner.java
@@ -35,14 +35,12 @@
 import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.Device;
-import org.onosproject.net.topology.LinkWeight;
 import org.onosproject.net.topology.PathService;
-import org.onosproject.net.topology.TopologyEdge;
-import org.onosproject.net.topology.TopologyService;
 import org.onosproject.newoptical.api.OpticalConnectivityId;
 import org.onosproject.newoptical.api.OpticalPathEvent;
 import org.onosproject.newoptical.api.OpticalPathListener;
 import org.onosproject.newoptical.api.OpticalPathService;
+import org.onosproject.net.topology.TopologyService;
 import org.onosproject.net.DefaultLink;
 import org.onosproject.net.provider.ProviderId;
 import org.slf4j.Logger;
@@ -133,39 +131,39 @@
         factories.forEach(cfgRegistry::unregisterConfigFactory);
     }
 
-    // TODO: Get FC as input
-    public void setupConnectivity(Set<CarrierEthernetNetworkInterface> niSet, CarrierEthernetVirtualConnection evc) {
+    public void setupConnectivity(CarrierEthernetForwardingConstruct fc) {
 
         boolean allPairsConnected = true;
 
         HashMap<CarrierEthernetNetworkInterface, HashSet<CarrierEthernetNetworkInterface>> ingressEgressNiMap =
                 new HashMap<>();
 
-        // Temporary set for iterating through NI pairs
-        Set<CarrierEthernetNetworkInterface> tempNiSet = new HashSet<>(niSet);
+        // Temporary set for iterating through LTP pairs
+        Set<CarrierEthernetLogicalTerminationPoint> tempLtpSet = new HashSet<>(fc.ltpSet());
 
-        // Temporary set for indicating which NIs were finally included
-        Set<CarrierEthernetNetworkInterface> usedNiSet = new HashSet<>();
+        // Temporary set for indicating which LTPs were finally included
+        Set<CarrierEthernetLogicalTerminationPoint> usedLtpSet = new HashSet<>();
 
-        Iterator<CarrierEthernetNetworkInterface> niIt1 = tempNiSet.iterator();
-        while (niIt1.hasNext()) {
+        Iterator<CarrierEthernetLogicalTerminationPoint> ltpIt1 = tempLtpSet.iterator();
+        while (ltpIt1.hasNext()) {
 
-            CarrierEthernetNetworkInterface ni1 = niIt1.next();
+            CarrierEthernetLogicalTerminationPoint ltp1 = ltpIt1.next();
 
             // Iterate through all the remaining NIs
-            Iterator<CarrierEthernetNetworkInterface> niIt2 = tempNiSet.iterator();
-            while (niIt2.hasNext()) {
+            Iterator<CarrierEthernetLogicalTerminationPoint> ltpIt2 = tempLtpSet.iterator();
+            while (ltpIt2.hasNext()) {
 
-                CarrierEthernetNetworkInterface ni2 = niIt2.next();
+                CarrierEthernetLogicalTerminationPoint ltp2 = ltpIt2.next();
 
                 // Skip equals
-                if (ni1.equals(ni2)) {
+                if (ltp1.equals(ltp2)) {
                     continue;
                 }
 
                 // Do not establish connectivity between leaf NIs (applies to Rooted_Multipoint)
-                if (ni1.role().equals(CarrierEthernetUni.Role.LEAF)
-                        && ni2.role().equals(CarrierEthernetUni.Role.LEAF)) {
+                // FIXME: Use proper LTP roles
+                if (ltp1.role().equals(CarrierEthernetLogicalTerminationPoint.Role.LEAF)
+                        && ltp2.role().equals(CarrierEthernetLogicalTerminationPoint.Role.LEAF)) {
                     continue;
                 }
 
@@ -175,88 +173,88 @@
 
                     Bandwidth reqBw;
 
-                    if (ni1.type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
-                        reqBw = ((CarrierEthernetUni) ni1).bwp().cir();
-                    } else if (ni2.type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
-                        reqBw = ((CarrierEthernetUni) ni2).bwp().cir();
+                    if (ltp1.type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
+                        reqBw = ((CarrierEthernetUni) ltp1.ni()).bwp().cir();
+                    } else if (ltp2.type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
+                        reqBw = ((CarrierEthernetUni) ltp2.ni()).bwp().cir();
                     } else {
                         reqBw = Bandwidth.bps((double) 0);
                     }
 
-                    opticalConnectId = setupOpticalConnectivity(ni1.cp(), ni2.cp(), reqBw, evc.latency());
+                    opticalConnectId = setupOpticalConnectivity(ltp1.ni().cp(), ltp2.ni().cp(), reqBw, fc.maxLatency());
 
                     if (opticalConnectId == null ||
                             opticalConnectStatusMap.get(opticalConnectId) != OpticalPathEvent.Type.PATH_INSTALLED) {
                         log.error("Could not establish optical connectivity between {} and {}" +
-                                        " (optical id and status: {}, {})", ni1.cp(), ni2.cp(), opticalConnectId,
+                                        " (optical id and status: {}, {})",
+                                ltp1.ni().cp(), ltp2.ni().cp(), opticalConnectId,
                                 (opticalConnectId == null ? "null" : opticalConnectStatusMap.get(opticalConnectId)));
                         allPairsConnected = false;
                         continue;
                     }
 
                     if (opticalConnectId != null) {
-                        evc.setMetroConnectivityId(opticalConnectId);
-                        evc.setMetroConnectivityStatus(opticalConnectStatusMap.get(opticalConnectId));
+                        fc.setMetroConnectivityId(opticalConnectId);
+                        fc.setMetroConnectivityStatus(opticalConnectStatusMap.get(opticalConnectId));
                     }
 
-                    log.info("Metro connectivity id and status for EVC {}: {}, {}", evc.id(),
-                            evc.metroConnectivity().id(), evc.metroConnectivity().status());
+                    log.info("Metro connectivity id and status for FC {}: {}, {}", fc.id(),
+                            fc.metroConnectivity().id(), fc.metroConnectivity().status());
 
                     if (opticalConnectId != null) {
                         // TODO: find vlanIds for both CO and store to service
                         opticalPathService.getPath(opticalConnectId).ifPresent(links -> {
                             getVlanTag(links).ifPresent(vlan -> {
-                                log.info("VLAN ID {} is assigned to CE service {}", vlan, evc.id());
-                                evc.setVlanId(vlan);
+                                log.info("VLAN ID {} is assigned to CE service {}", vlan, fc.id());
+                                fc.setVlanId(vlan);
                             });
                             getTransportVlanTag(links).ifPresent(vlan -> {
-                                log.info("Transport VLAN ID {} is assigned to CE service {}", vlan, evc.id());
-                                evc.setTransportVlanId(vlan);
+                                log.info("Transport VLAN ID {} is assigned to CE service {}", vlan, fc.id());
+                                fc.setTransportVlanId(vlan);
                             });
                         });
                     }
                 }
 
-                // Update the cpPathHashSet based on the calculated paths
-                if (!updateIngressEgressNiMap(ni1, ni2, ingressEgressNiMap, evc.congruentPaths(), evc.type())) {
+                // Update the ingress-egress NI map based on the calculated paths
+                if (!updateIngressEgressNiMap(ltp1.ni(), ltp2.ni(), ingressEgressNiMap,
+                        fc.congruentPaths(), fc.type())) {
                     removeOpticalConnectivity(opticalConnectId);
                     allPairsConnected = false;
                     continue;
                 }
 
                 // Indicate that connection for at least one NI pair has been established
-                evc.setState(CarrierEthernetVirtualConnection.State.ACTIVE);
+                fc.setState(CarrierEthernetForwardingConstruct.State.ACTIVE);
 
                 // Add NIs to the set of NIs used by the EVC
-                usedNiSet.add(ni1);
-                usedNiSet.add(ni2);
+                usedLtpSet.add(ltp1);
+                usedLtpSet.add(ltp2);
             }
             // Remove NI from temporary set so that each pair is visited only once
-            niIt1.remove();
+            ltpIt1.remove();
         }
 
         // Establish connectivity using the ingressEgressNiMap
         ingressEgressNiMap.keySet().forEach(srcNi -> {
             // Set forwarding only on packet switches
             if (deviceService.getDevice(srcNi.cp().deviceId()).type().equals(Device.Type.SWITCH)) {
-                ceOfPktNodeManager.setNodeForwarding(evc, srcNi, ingressEgressNiMap.get(srcNi));
+                ceOfPktNodeManager.setNodeForwarding(fc, srcNi, ingressEgressNiMap.get(srcNi));
             }
         });
 
         // Update the NI set, based on the NIs actually used
-        evc.setNiSet(usedNiSet);
+        fc.setLtpSet(usedLtpSet);
 
-        if (evc.state().equals(CarrierEthernetVirtualConnection.State.ACTIVE)) {
-            if (allPairsConnected) {
-                evc.setActiveState(CarrierEthernetVirtualConnection.ActiveState.FULL);
-            } else {
-                evc.setActiveState(CarrierEthernetVirtualConnection.ActiveState.PARTIAL);
+        if (fc.isActive()) {
+            if (!allPairsConnected) {
+                fc.setState(CarrierEthernetConnection.State.PARTIAL);
             }
         }
     }
 
     /**
-     * Select a feasible link path between two NIs
+     * Select feasible link paths between two NIs in both directions and update ingressEgressNiMap accordingly
      *
      * @param ni1 the first NI
      * @param ni2 the second NI
@@ -270,6 +268,7 @@
                                 boolean congruentPaths, CarrierEthernetVirtualConnection.Type evcType) {
 
         // Find the paths for both directions at the same time, so that we can skip the pair if needed
+        // TODO: Handle the case when ni1 and ni2 are on the same device - probably in the generateLinkList
         List<Link> forwardLinks = generateLinkList(ni1.cp(), ni2.cp(), evcType);
         List<Link> backwardLinks =
                 congruentPaths ? generateInverseLinkList(forwardLinks) : generateLinkList(ni2.cp(), ni1.cp(), evcType);
@@ -294,17 +293,25 @@
                                             HashMap<CarrierEthernetNetworkInterface,
                                                     HashSet<CarrierEthernetNetworkInterface>> ingressEgressNiMap
                                             ) {
+        // FIXME: Fix the method - avoid generating GENERIC NIs if not needed
         // Add the src and destination NIs as well as the associated Generic NIs
         ingressEgressNiMap.putIfAbsent(srcNi, new HashSet<>());
-        ingressEgressNiMap.get(srcNi).add(new CarrierEthernetGenericNi(linkList.get(1).src(), null));
-        CarrierEthernetGenericNi ingressNi =
-                new CarrierEthernetGenericNi(linkList.get(linkList.size() - 2).dst(), null);
-        ingressEgressNiMap.putIfAbsent(ingressNi, new HashSet<>());
-        ingressEgressNiMap.get(ingressNi).add(dstNi);
+        // Add last hop entry only if srcNi, dstNi aren't on same device (in which case srcNi, ingressNi would coincide)
+        if (!srcNi.cp().deviceId().equals(dstNi.cp().deviceId())) {
+            // If srcNi, dstNi are not on the same device, create mappings to/from new GENERIC NIs
+            ingressEgressNiMap.get(srcNi).add(new CarrierEthernetGenericNi(linkList.get(1).src(), null));
+            CarrierEthernetGenericNi ingressNi =
+                    new CarrierEthernetGenericNi(linkList.get(linkList.size() - 2).dst(), null);
+            ingressEgressNiMap.putIfAbsent(ingressNi, new HashSet<>());
+            ingressEgressNiMap.get(ingressNi).add(dstNi);
+        } else {
+            // If srcNi, dstNi are on the same device, this is the only mapping that will be created
+            ingressEgressNiMap.get(srcNi).add(dstNi);
+        }
 
         // Go through the links and create/add the intermediate NIs
         for (int i = 1; i < linkList.size() - 2; i++) {
-            ingressNi = new CarrierEthernetGenericNi(linkList.get(i).dst(), null);
+            CarrierEthernetGenericNi ingressNi = new CarrierEthernetGenericNi(linkList.get(i).dst(), null);
             ingressEgressNiMap.putIfAbsent(ingressNi, new HashSet<>());
             ingressEgressNiMap.get(ingressNi).add(new CarrierEthernetGenericNi(linkList.get(i + 1).src(), null));
         }
@@ -312,28 +319,35 @@
 
     private List<Link> generateLinkList(ConnectPoint cp1, ConnectPoint cp2,
                                         CarrierEthernetVirtualConnection.Type evcType) {
-
         Set<Path> paths;
-        if (evcType.equals(CarrierEthernetVirtualConnection.Type.POINT_TO_POINT)) {
-            // For point-to-point connectivity use the pre-calculated paths to make sure the shortest paths are chosen
-            paths = pathService.getPaths(cp1.deviceId(), cp2.deviceId());
-        } else {
-            // Recalculate path so that it's over the pre-calculated spanning tree
-            // FIXME: Find a more efficient way (avoid recalculating paths)
-            paths = pathService.getPaths(cp1.deviceId(), cp2.deviceId(), new SpanningTreeWeight());
-        }
+        Path path = null;
 
-        // Just select any of the returned paths
-        // TODO: Select path in more sophisticated way and return null if any of the constraints cannot be met
-        Path path = paths.iterator().hasNext() ? paths.iterator().next() : null;
+        if (!cp1.deviceId().equals(cp2.deviceId())) {
+            // If cp1 and cp2 are not on the same device a path must be found
+            if (evcType.equals(CarrierEthernetVirtualConnection.Type.POINT_TO_POINT)) {
+                // For point-to-point connectivity use pre-calculated paths to make sure the shortest paths are chosen
+                paths = pathService.getPaths(cp1.deviceId(), cp2.deviceId());
+            } else {
+                // Recalculate path so that it's over the pre-calculated spanning tree
+                // FIXME: Find a more efficient way (avoid recalculating paths)
+                paths = pathService.getPaths(cp1.deviceId(), cp2.deviceId(),
+                        new CarrierEthernetSpanningTreeWeight(topologyService));
+            }
 
-        if (path == null) {
-            return null;
+            // Just select any of the returned paths
+            // TODO: Select path in more sophisticated way and return null if any of the constraints cannot be met
+            path = paths.iterator().hasNext() ? paths.iterator().next() : null;
+
+            if (path == null) {
+                return null;
+            }
         }
 
         List<Link> links = new ArrayList<>();
         links.add(createEdgeLink(cp1, true));
-        links.addAll(path.links());
+        if (!cp1.deviceId().equals(cp2.deviceId())) {
+            links.addAll(path.links());
+        }
         links.add(createEdgeLink(cp2, false));
 
         return links;
@@ -363,31 +377,34 @@
         return inverseLinks;
     }
 
-    public void removeConnectivity(CarrierEthernetVirtualConnection evc) {
+    public void removeConnectivity(CarrierEthernetForwardingConstruct fc) {
         // TODO: Add here the same call for all node manager types
-        ceOfPktNodeManager.removeAllForwardingResources(evc);
-        removeOpticalConnectivity(evc.metroConnectivity().id());
+        ceOfPktNodeManager.removeAllForwardingResources(fc);
+        removeOpticalConnectivity(fc.metroConnectivity().id());
     }
 
     /**
-     * Applies bandwidth profiles to the UNIs of an EVC.
+     * Applies bandwidth profiles to the UNIs of an FC.
      *
-     * @param evc the EVC representation
+     * @param fc the FC representation
      */
-    public void applyBandwidthProfiles(CarrierEthernetVirtualConnection evc) {
+    public void applyBandwidthProfiles(CarrierEthernetForwardingConstruct fc) {
         //  TODO: Select node manager depending on device protocol
-        evc.niSet().forEach((uni -> ceOfPktNodeManager.applyBandwidthProfileResources(evc, (CarrierEthernetUni) uni)));
+        fc.uniSet().forEach(uni -> ceOfPktNodeManager.applyBandwidthProfileResources(fc, uni));
     }
 
     /**
-     * Removes bandwidth profiles from the UNIs of an EVC.
+     * Removes bandwidth profiles from the UNIs of an FC.
      *
-     * @param evc the EVC representation
+     * @param fc the FC representation
      */
-    public void removeBandwidthProfiles(CarrierEthernetVirtualConnection evc) {
+    public void removeBandwidthProfiles(CarrierEthernetForwardingConstruct fc) {
         //  TODO: Select node manager depending on device protocol
-        evc.niSet().forEach(uni -> ceOfPktNodeManager
-                .removeBandwidthProfileResources(evc.id(), (CarrierEthernetUni) uni));
+        fc.ltpSet().forEach((ltp -> {
+            if (ltp.ni().type().equals(CarrierEthernetNetworkInterface.Type.UNI)) {
+                ceOfPktNodeManager.removeBandwidthProfileResources(fc.id(), (CarrierEthernetUni) ltp.ni());
+            }
+        }));
     }
 
     private class OpticalEventListener implements OpticalPathListener {
@@ -511,31 +528,4 @@
         }
 
     }
-
-    /**
-     * Checks if a connect point is on the pre-calculated spanning tree.
-     *
-     * @param cp the connect point to check
-     * @return true if the connect point is on the spanning tree and false otherwise
-     */
-    private boolean isBroadCastPoint(ConnectPoint cp) {
-        // TODO: Get topology snapshot so that same spanning tree is used by all pairs if topology changes
-        return topologyService.isBroadcastPoint(topologyService.currentTopology(), cp);
-    }
-
-    /**
-     * Weight class to cause path selection only on the pre-calculated spanning tree.
-     */
-    private class SpanningTreeWeight implements LinkWeight {
-
-        @Override
-        public double weight(TopologyEdge edge) {
-            if (!isBroadCastPoint(edge.link().src()) || !isBroadCastPoint(edge.link().dst())) {
-                return -1;
-            } else {
-                return 1;
-            }
-        }
-    }
-
 }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetSpanningTreeWeight.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetSpanningTreeWeight.java
new file mode 100644
index 0000000..c5f5e4a
--- /dev/null
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetSpanningTreeWeight.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ecord.carrierethernet.app;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.topology.LinkWeight;
+import org.onosproject.net.topology.TopologyEdge;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Weight class to cause path selection only on the pre-calculated spanning tree.
+ */
+public class CarrierEthernetSpanningTreeWeight implements LinkWeight {
+
+    private final Logger log = getLogger(getClass());
+
+    protected TopologyService topologyService = null;
+
+    public CarrierEthernetSpanningTreeWeight(TopologyService topologyService) {
+        this.topologyService = topologyService;
+    }
+
+    @Override
+    public double weight(TopologyEdge edge) {
+        if (!isBroadCastPoint(edge.link().src()) || !isBroadCastPoint(edge.link().dst())) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+
+    /**
+     * Checks if a connect point is on the pre-calculated spanning tree.
+     *
+     * @param cp the connect point to check
+     * @return true if the connect point is on the spanning tree and false otherwise
+     */
+    private boolean isBroadCastPoint(ConnectPoint cp) {
+        // TODO: Get topology snapshot so that same spanning tree is used by all pairs if topology changes
+        if (cp == null) {
+            log.info("Cp is null!");
+        }
+        if (topologyService == null) {
+            log.info("topologyservice is null!");
+        }
+        return topologyService.isBroadcastPoint(topologyService.currentTopology(), cp);
+    }
+}
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
index 4c75831..75ac1b6 100644
--- 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
@@ -38,7 +38,7 @@
  * 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 extends CarrierEthernetNetworkInterface {
+public class CarrierEthernetUni extends CarrierEthernetNetworkInterface <CarrierEthernetUni> {
 
     private final Logger log = getLogger(getClass());
 
@@ -109,11 +109,26 @@
     }
 
     /**
-     * Adds the resources associated with an EVC-specific UNI to a global UNI.
+     * Adds a BW profile to a UNI.
      *
-     * @param uni the EVC UNI to be added
+     * @param bwp the BWP to be added
      */
-    public void addEvcUni(CarrierEthernetUni uni) {
+    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()));
+    }
+
+    /**
+     * Adds the resources associated with an EVC- or FC-specific UNI to a global UNI.
+     *
+     * @param uni the EVC- or FC-specific UNI to be added
+     */
+    @Override
+    public void addEcNi(CarrierEthernetUni uni) {
 
         // Add CE-VLAN ID
         if (uni.ceVlanId() != VlanId.NONE) {
@@ -130,25 +145,12 @@
     }
 
     /**
-     * Adds a BW profile to a UNI.
+     * Removes the resources associated with an EVC- or FC-specific UNI from a global UNI.
      *
-     * @param bwp the BWP to be added
+     * @param uni the EVC- or FC-specific UNI to be removed
      */
-    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 removeEvcUni(CarrierEthernetUni uni) {
+    @Override
+    public void removeEcNi(CarrierEthernetUni uni) {
 
         // Remove UNI CE-VLAN ID
         if (uni.ceVlanId() != VlanId.NONE) {
@@ -165,12 +167,13 @@
     }
 
     /**
-     * Validates whether an EVC-specific UNI is compatible with a global UNI.
+     * Validates whether an EVC- or FC-specific UNI is compatible with the corresponding global UNI.
      *
-     * @param uni the EVC-specific UNI
+     * @param uni the EVC- or FC-specific UNI
      * @return boolean value indicating whether the UNIs are compatible
      */
-    public boolean validateEvcUni(CarrierEthernetUni uni) {
+    @Override
+    public boolean validateEcNi(CarrierEthernetUni uni) {
 
         // Check if the CE-VLAN ID of the UNI is already included in global UNI
         if (uni.ceVlanId() != VlanId.NONE) {
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetVirtualConnection.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetVirtualConnection.java
index 81c7aed..fb58a8b 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetVirtualConnection.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetVirtualConnection.java
@@ -15,197 +15,46 @@
  */
 package org.onosproject.ecord.carrierethernet.app;
 
-import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableSet;
-import org.onlab.packet.VlanId;
-import org.onosproject.newoptical.api.OpticalConnectivityId;
-import org.onosproject.newoptical.api.OpticalPathEvent;
 
-import java.time.Duration;
 import java.util.HashSet;
 import java.util.Set;
+import java.time.Duration;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 
 /**
  * Representation of a Carrier Ethernet EVC.
  */
-public class CarrierEthernetVirtualConnection {
+public class CarrierEthernetVirtualConnection extends CarrierEthernetConnection {
 
-    public enum Type {
-
-        POINT_TO_POINT("Point_To_Point"),
-        MULTIPOINT_TO_MULTIPOINT("Multipoint_To_Multipoint"),
-        ROOT_MULTIPOINT("Root_Multipoint");
-
-        private String value;
-
-        Type(String value) {
-            this.value = value;
-        }
-
-        @Override
-        public String toString() {
-            return value;
-        }
-
-        public static Type fromString(String value) {
-            if (value != null) {
-                for (Type b : Type.values()) {
-                    if (value.equals(b.value)) {
-                        return b;
-                    }
-                }
-            }
-            throw new IllegalArgumentException("Type " + value + " is not valid");
-        }
-    }
-
-    public enum State {
-
-        ACTIVE("Active"),
-        INACTIVE("Inactive");
-
-        private String value;
-
-        State(String value) {
-            this.value = value;
-        }
-
-        @Override
-        public String toString() {
-            return value;
-        }
-
-        public static State fromString(String value) {
-            if (value != null) {
-                for (State b : State.values()) {
-                    if (value.equals(b.value)) {
-                        return b;
-                    }
-                }
-            }
-            throw new IllegalArgumentException("State " + value + " is not valid");
-        }
-    }
-
-    public enum ActiveState {
-
-        FULL("Full"),
-        PARTIAL("Partial");
-
-        private String value;
-
-        ActiveState(String value) {
-            this.value = value;
-        }
-
-        @Override
-        public String toString() {
-            return value;
-        }
-    }
-
-    // FIXME: single vlanId is a hack for ONS2016.  CE service must store vlanId for each CO.
-    protected String evcId;
-    protected String evcCfgId;
-    protected Type evcType;
-    protected State evcState;
-    protected ActiveState evcActiveState;
-    protected VlanId vlanId;
-    private VlanId transportVlanId;
-    protected boolean isVirtual;
-    protected Integer maxNumUni;
-    protected Set<CarrierEthernetNetworkInterface> niSet;
-    protected Duration latency;
-    protected CarrierEthernetMetroConnectivity 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);
+    private Short shortId;
+    private Set<CarrierEthernetUni> uniSet;
+    private boolean isVirtual;
+    private Integer maxNumUni;
+    private Set<CarrierEthernetForwardingConstruct> fcSet;
 
     // Maximum possible number of UNIs for non-Point-to-Point EVCs
     public static final Integer MAX_NUM_UNI = 1000;
 
     // Note: evcId should be provided only when updating an existing service
-    public CarrierEthernetVirtualConnection(String evcId, String evcCfgId, Type evcType, Integer maxNumUni,
-                                            Set<CarrierEthernetNetworkInterface> niSet) {
-        this.evcId = evcId;
-        this.evcCfgId = evcCfgId;
-        this.evcType = evcType;
-        this.evcState = State.INACTIVE;
-        this.evcActiveState = null;
-        this.maxNumUni = (maxNumUni != null ? maxNumUni : (evcType.equals(Type.POINT_TO_POINT) ? 2 : MAX_NUM_UNI));
-        this.vlanId = null;
-        this.niSet = new HashSet<>(niSet);
-        this.congruentPaths = CONGRUENT_PATHS;
-        this.latency = DEFAULT_LATENCY;
-        this.metroConnectivity = new CarrierEthernetMetroConnectivity(null, OpticalPathEvent.Type.PATH_REMOVED);
+    public CarrierEthernetVirtualConnection(String id, String cfgId, Type type, Integer maxNumUni,
+                                            Set<CarrierEthernetUni> uniSet,
+                                            Duration maxLatency) {
+        super(id, cfgId, type, maxLatency);
+        this.maxNumUni = (maxNumUni != null ? maxNumUni : (type.equals(Type.POINT_TO_POINT) ? 2 : MAX_NUM_UNI));
+        this.uniSet = new HashSet<>(uniSet);
+        this.fcSet = new HashSet<>();
+        this.shortId = null;
     }
 
     /**
-     * Returns service identifier.
+     * Returns numerical identifier.
      *
-     * @return service identifier
+     * @return numerical identifier
      */
-    public String id() {
-        return evcId;
-    }
-
-    /**
-     * Returns service config identifier.
-     *
-     * @return service config identifier
-     */
-    public String cfgId() {
-        return evcCfgId;
-    }
-
-    /**
-     * Returns type of service.
-     *
-     * @return type of service
-     */
-    public Type type() {
-        return evcType;
-    }
-
-    /**
-     * Returns connectivity state of the EVC.
-     *
-     * @return connectivity state
-     */
-    public State state() {
-        return evcState;
-    }
-
-    /**
-     * Returns active connectivity state of the EVC.
-     *
-     * @return active connectivity state
-     */
-    public ActiveState activeState() {
-        return evcActiveState;
-    }
-
-    /**
-     * Returns Vlan id.
-     *
-     * @return Vlan id
-     */
-    public VlanId vlanId() {
-        return vlanId;
-    }
-
-    /**
-     * Returns Transport Vlan ID.
-     *
-     * @return Transport Vlan ID.
-     */
-    @Beta
-    public VlanId transportVlanId() {
-        return transportVlanId;
+    public Short shortId() {
+        return shortId;
     }
 
     /**
@@ -229,95 +78,44 @@
      *
      * @return set of UNIs
      */
-    public Set<CarrierEthernetNetworkInterface> niSet() {
-        return ImmutableSet.copyOf(niSet);
+    public Set<CarrierEthernetUni> uniSet() {
+        return ImmutableSet.copyOf(uniSet);
     }
 
     /**
-     * Returns latency constraint.
+     * Returns the set of FCs associated with the EVC.
      *
-     * @return latency constraint
+     * @return set of FCs associated with the EVC
      */
-    public Duration latency() {
-        return latency;
+    public Set<CarrierEthernetForwardingConstruct> fcSet() {
+        return ImmutableSet.copyOf(fcSet);
     }
 
     /**
-     * Returns true if service requires congruent paths.
+     * Set numerical identifier.
      *
-     * @return true if congruent paths required
+     * @param shortId the numerical identifier to set
      */
-    public boolean congruentPaths() {
-        return congruentPaths;
-    }
-
-    /**
-     * Sets service identifier.
-     *
-     * @param serviceId the service identifier to set
-     */
-    public void setId(String serviceId) {
-        this.evcId = serviceId;
-    }
-
-    /**
-     * Sets service config identifier.
-     *
-     * @param serviceCfgId service config identifier
-     */
-    public void setCfgId(String serviceCfgId) {
-        this.evcCfgId = serviceCfgId;
+    public void setShortId(Short shortId) {
+        this.shortId = shortId;
     }
 
     /**
      * Sets the set of UNIs.
      *
-     * @param niSet the set of UNIs to be set
+     * @param uniSet the set of UNIs to be set
      */
-    public void setNiSet(Set<CarrierEthernetNetworkInterface> niSet) {
-        this.niSet = niSet;
+    public void setUniSet(Set<CarrierEthernetUni> uniSet) {
+        this.uniSet = uniSet;
     }
 
     /**
-     * Sets the connectivity state of the EVC.
+     * Sets the set of FCs.
      *
-     * @param evcState the connectivity state to set
+     * @param fcSet the set of UNIs to be set
      */
-    public void setState(State evcState) { this.evcState = evcState; }
-
-    /**
-     * Sets the active connectivity state of the EVC.
-     *
-     * @param evcActiveState the active connectivity state to set
-     */
-    public void setActiveState(ActiveState evcActiveState) { this.evcActiveState = evcActiveState; }
-
-    /**
-     * 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 vlanId to be used by the transport.
-     *
-     * @param vlan the vlanId to set
-     */
-    @Beta
-    public void setTransportVlanId(VlanId vlan) {
-        this.transportVlanId = vlan;
+    public void setFcSet(Set<CarrierEthernetForwardingConstruct> fcSet) {
+        this.fcSet = fcSet;
     }
 
     /**
@@ -329,44 +127,15 @@
         this.isVirtual = isVirtual;
     }
 
-    /**
-     * Gets metro connectivity id.
-     *
-     * @return the metro connectivity of the service
-     */
-    public CarrierEthernetMetroConnectivity metroConnectivity() {
-        return this.metroConnectivity;
-    }
-
-    /**
-     * Sets metro connectivity id.
-     *
-     * @param id the metro connectivity identifier to set
-     */
-    public void setMetroConnectivityId(OpticalConnectivityId id) {
-        this.metroConnectivity.setId(id);
-    }
-
-    /**
-     * Sets metro connectivity status.
-     *
-     * @param status the metro connectivity status
-     */
-    public void setMetroConnectivityStatus(OpticalPathEvent.Type status) {
-        this.metroConnectivity.setStatus(status);
-    }
-
     @Override
     public String toString() {
 
         return toStringHelper(this)
                 .omitNullValues()
-                .add("id", evcId)
-                .add("cfgId", evcCfgId)
-                .add("type", evcType)
-                .add("vlanId", vlanId)
-                .add("transportVlanId", transportVlanId)
-                .add("metroConnectId", (metroConnectivity.id() == null ? "null" : metroConnectivity.id().id()))
-                .add("NIs", niSet).toString();
+                .add("id", id)
+                .add("cfgId", cfgId)
+                .add("type", type)
+                .add("UNIs", uniSet)
+                .add("FCs", fcSet).toString();
     }
 }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateEvcCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateEvcCommand.java
index c0f663b..b95236b 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateEvcCommand.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateEvcCommand.java
@@ -34,7 +34,7 @@
 import java.util.Set;
 
 /**
- * CLI command for installing EVCs.
+ * CLI command for installing an Ethernet Virtual Connection.
  */
 @Command(scope = "onos", name = "ce-evc-create",
          description = "Carrier Ethernet EVC creation command.")
@@ -80,9 +80,9 @@
         CarrierEthernetManager ceManager = get(CarrierEthernetManager.class);
 
         CarrierEthernetVirtualConnection evc = new CarrierEthernetVirtualConnection(argEvcId, argEvcCfgId,
-                generateEvcType(), generateMaxNumUni(), generateUniSet());
+                generateEvcType(), generateMaxNumUni(), generateUniSet(), null);
 
-        ceManager.establishConnectivity(evc);
+        ceManager.installEvc(evc);
     }
 
     /**
@@ -159,9 +159,9 @@
      *
      * @return the set of UNIs for the CE EVC
      */
-    Set<CarrierEthernetNetworkInterface> generateUniSet() {
+    Set<CarrierEthernetUni> generateUniSet() {
 
-        Set<CarrierEthernetNetworkInterface> uniSet = new HashSet<>();
+        Set<CarrierEthernetUni> uniSet = new HashSet<>();
 
         CarrierEthernetVirtualConnection.Type evcType = generateEvcType();
 
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateFcCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateFcCommand.java
index d9a7126..ee7f058 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateFcCommand.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateFcCommand.java
@@ -30,7 +30,7 @@
 import java.util.Set;
 
 /**
- * CLI command for generating Carrier Ethernet Forwarding Constructs.
+ * CLI command for installing a Carrier Ethernet Forwarding Construct.
  */
 @Command(scope = "onos", name = "ce-fc-create",
         description = "Creates and installs a Carrier Ethernet Forwarding Construct.")
@@ -75,8 +75,8 @@
 
         CarrierEthernetManager ceManager = get(CarrierEthernetManager.class);
 
-        CarrierEthernetForwardingConstruct fc = new CarrierEthernetForwardingConstruct(argFcId, argFcCfgId, null,
-                generateServiceType(), generateLtpSet());
+        CarrierEthernetForwardingConstruct fc = new CarrierEthernetForwardingConstruct(argFcId, argFcCfgId,
+                generateServiceType(), generateLtpSet(), null);
 
         ceManager.installFc(fc);
     }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateLtpCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateLtpCommand.java
index 4c957d0..a519111 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateLtpCommand.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateLtpCommand.java
@@ -28,7 +28,7 @@
 import java.util.Set;
 
 /**
- * CLI command for generating Carrier Ethernet Logical Termination Points.
+ * CLI command for generating one or more Carrier Ethernet Logical Termination Points.
  */
 @Command(scope = "onos", name = "ce-ltp-create",
         description = "Creates Carrier Ethernet Logical Termination Points.")
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateUniCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateUniCommand.java
index ea02b81..f500d29 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateUniCommand.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetCreateUniCommand.java
@@ -26,7 +26,7 @@
 import java.util.List;
 
 /**
- * CLI command for generating Carrier Ethernet UNIs.
+ * CLI command for generating one or more Carrier Ethernet UNIs.
  */
 @Command(scope = "onos", name = "ce-uni-create",
         description = "Creates Carrier Ethernet UNIs.")
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveEvcCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveEvcCommand.java
index b17afb2..dba5dcf 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveEvcCommand.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveEvcCommand.java
@@ -24,7 +24,7 @@
 import java.util.List;
 
 /**
- * CLI command for removing installed EVCs.
+ * CLI command for removing one or more installed EVCs.
  */
 @Command(scope = "onos", name = "ce-evc-remove",
         description = "Carrier Ethernet EVC removal command.")
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveFcCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveFcCommand.java
index 873925d..f07cc37 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveFcCommand.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveFcCommand.java
@@ -15,24 +15,28 @@
  */
 package org.onosproject.ecord.carrierethernet.cli.commands;
 
+import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.ecord.carrierethernet.app.CarrierEthernetManager;
 
+import java.util.List;
+
 /**
- * CLI command for removing an installed Carrier Ethernet Forwarding Construct.
+ * CLI command for removing one or more installed Carrier Ethernet Forwarding Constructs.
  */
 @Command(scope = "onos", name = "ce-fc-remove",
         description = "Carrier Ethernet service removal command.")
 public class CarrierEthernetRemoveFcCommand extends AbstractShellCommand {
 
-    @Argument(index = 0, name = "argFcId", description = "Forwarding Construct ID", required = true, multiValued = false)
-    String argFcId = null;
+    @Argument(index = 0, name = "argFcIdList", description = "Forwarding Construct IDs",
+            required = true, multiValued = true)
+    List<String> argFcIdList = Lists.newArrayList();
 
     @Override
     protected void execute() {
         CarrierEthernetManager ceManager = get(CarrierEthernetManager.class);
-        ceManager.removeFc(argFcId);
+        argFcIdList.forEach(argFcId -> ceManager.removeFc(argFcId));
     }
 }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveLtpCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveLtpCommand.java
index dc84ef5..6163430 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveLtpCommand.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveLtpCommand.java
@@ -24,7 +24,7 @@
 import java.util.List;
 
 /**
- * CLI command for removing LTPs.
+ * CLI command for removing one or multiple LTPs.
  */
 @Command(scope = "onos", name = "ce-ltp-remove",
         description = "Carrier Ethernet LTP removal command.")
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveUniCommand.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveUniCommand.java
index 701a447..dd47ba8 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveUniCommand.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/cli/commands/CarrierEthernetRemoveUniCommand.java
@@ -24,7 +24,7 @@
 import java.util.List;
 
 /**
- * CLI command for removing UNIs.
+ * CLI command for removing one or more UNIs.
  */
 @Command(scope = "onos", name = "ce-uni-remove",
         description = "Carrier Ethernet UNI removal command.")