Porting CE app to 4.1

Change-Id: Ic3280ce797f6225773ade789d9793ae445f9f525
diff --git a/global/virtualprovider/pom.xml b/global/virtualprovider/pom.xml
new file mode 100644
index 0000000..0438e93
--- /dev/null
+++ b/global/virtualprovider/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-present Open Networking Foundation
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.opencord.ce</groupId>
+        <artifactId>global</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>virtualprovider</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>Topology provider in the global orchestrator</description>
+
+    <properties>
+        <onos.app.name>org.opencord.ce.global.vprovider</onos.app.name>
+        <onos.version>1.10.6</onos.version>
+        <onos.app.url>http://opencord.org</onos.app.url>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <repositories>
+        <repository>
+            <id>snapshots</id>
+            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+            <releases><enabled>false</enabled></releases>
+        </repository>
+    </repositories>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>snapshots</id>
+            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+            <releases><enabled>false</enabled></releases>
+        </pluginRepository>
+    </pluginRepositories>
+
+</project>
diff --git a/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/VirtualDomainDeviceProvider.java b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/VirtualDomainDeviceProvider.java
new file mode 100644
index 0000000..d1bdefb
--- /dev/null
+++ b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/VirtualDomainDeviceProvider.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.ce.global.virtualdomain;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.opencord.ce.api.services.virtualprovider.DomainVirtualDevice;
+import org.opencord.ce.api.services.virtualprovider.EcordDeviceProviderService;
+import org.onlab.packet.ChassisId;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipAdminService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceProvider;
+import org.onosproject.net.device.DeviceProviderRegistry;
+import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.domain.DomainId;
+import org.onosproject.net.provider.ProviderId;
+import org.opencord.ce.api.models.CarrierEthernetEnni;
+import org.opencord.ce.api.models.CarrierEthernetGenericNi;
+import org.opencord.ce.api.models.CarrierEthernetInni;
+import org.opencord.ce.api.models.CarrierEthernetLogicalTerminationPoint;
+import org.opencord.ce.api.models.CarrierEthernetNetworkInterface;
+import org.opencord.ce.api.models.CarrierEthernetUni;
+import org.opencord.ce.api.services.MetroOrchestrationService;
+import org.opencord.ce.global.virtualdomain.config.EcordDriverConfig;
+import org.opencord.ce.global.virtualdomain.config.XosEndPointConfig;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.AnnotationKeys.DRIVER;
+import static org.onosproject.net.AnnotationKeys.LATITUDE;
+import static org.onosproject.net.AnnotationKeys.LONGITUDE;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.opencord.ce.api.models.CarrierEthernetNetworkInterface.Type;
+
+/**
+ * Exposes remote domain devices to the core.
+ */
+@Component(immediate = true)
+@Service(value = EcordDeviceProviderService.class)
+public class VirtualDomainDeviceProvider
+        implements DeviceProvider, EcordDeviceProviderService {
+    private final Logger log = getLogger(getClass());
+    public static final String PROVIDER_NAME = "org.opencord.ce.global.vprovider";
+    public static final ProviderId PROVIDER_ID = new ProviderId("domain", PROVIDER_NAME);
+    private static final String UNKNOWN = "unknown";
+    private static final String NO_LLDP = "no-lldp";
+    private static final String DOMAIN_ID = "domainId";
+    private static final String MEF_IF_TYPE = "mefIfType";
+
+    private ApplicationId appId;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceProviderRegistry deviceProviderRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipAdminService mastershipAdminService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MetroOrchestrationService metroOrchestrationService;
+
+    protected DeviceProviderService deviceProviderService;
+
+    private EcordDriverConfig ecordDriverConfig;
+    private XoSHttpClient xoSHttpClient;
+
+    private final NetworkConfigListener configListener = new InternalConfigListener();
+
+    /*
+    private final ConfigFactory<ApplicationId, EcordDriverConfig> diverConfigFactory =
+            new ConfigFactory<ApplicationId, EcordDriverConfig>(APP_SUBJECT_FACTORY,
+                    EcordDriverConfig.class, "driver") {
+                @Override
+                public EcordDriverConfig createConfig() {
+                    return new EcordDriverConfig();
+                }
+            };
+            */
+
+    private final ConfigFactory<ApplicationId, XosEndPointConfig> xosEndpointConfigFactory =
+            new ConfigFactory<ApplicationId, XosEndPointConfig>(SubjectFactories.APP_SUBJECT_FACTORY,
+                    XosEndPointConfig.class, "xos") {
+                @Override
+                public XosEndPointConfig createConfig() {
+                    return new XosEndPointConfig();
+                }
+            };
+
+    private final ExecutorService eventExecutor =
+            newFixedThreadPool(3,
+                    groupedThreads("onos/ecord-sb-manager", "event-handler-%d"));
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication(PROVIDER_NAME);
+       // configRegistry.registerConfigFactory(diverConfigFactory);
+        configRegistry.registerConfigFactory(xosEndpointConfigFactory);
+        configService.addListener(configListener);
+        deviceProviderService = deviceProviderRegistry.register(this);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        //configRegistry.unregisterConfigFactory(diverConfigFactory);
+        configRegistry.unregisterConfigFactory(xosEndpointConfigFactory);
+        configService.removeListener(configListener);
+        deviceProviderRegistry.unregister(this);
+        log.info("Stopped");
+    }
+
+    @Override
+    public ProviderId id() {
+        return PROVIDER_ID;
+    }
+
+    // ==== DeviceProvider ====
+    @Override
+    public void triggerProbe(DeviceId deviceId) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
+
+    }
+
+    @Override
+    public boolean isReachable(DeviceId deviceId) {
+        // TODO
+        return true;
+    }
+
+    @Override
+    public void changePortState(DeviceId deviceId, PortNumber portNumber,
+                                boolean enable) {
+        // TODO
+    }
+
+    // ===== EcordDeviceProviderService =====
+    @Override
+    public void connectRemoteDevice(DomainVirtualDevice domainDevice) {
+        DomainId domainId = domainDevice.domainId();
+        DeviceId deviceId = domainDevice.deviceId();
+        advertiseDevice(deviceId, domainId);
+       // advertiseDevicePorts(deviceId, domainDevice.ports());
+    }
+
+    @Override
+    public void addOrUpdateRemotePorts(DomainId domainId, DeviceId deviceId,
+                                       List<PortDescription> ports) {
+        advertiseDevicePorts(deviceId, ports);
+    }
+
+    @Override
+    public void addRemotePort(DomainId domainId, DeviceId deviceId, Port newPort) {
+    }
+
+    @Override
+    public void updateRemotePortState(DomainId domainId, DeviceId deviceId, Port port) {
+        // TODO
+    }
+
+    @Override
+    public void disconnectRemoteDevice(DomainId domainId, DeviceId deviceId) {
+        disconnectDevice(deviceId);
+    }
+
+    @Override
+    public void removeRemotePort(DomainId domainId, DeviceId deviceId, PortNumber portNumber) {
+        // TODO
+    }
+
+    /**
+     * Notify the core system that a new domain device is on.
+     * @param deviceId remote device identifier
+     */
+    private void advertiseDevice(DeviceId deviceId, DomainId domainId) {
+        ChassisId chassisId = new ChassisId();
+        log.info("Notifying ecord virtual device...");
+        mastershipAdminService.setRole(clusterService.getLocalNode().id(), deviceId,
+                MastershipRole.MASTER);
+
+        // disable lldp for this virtual device and annotate it with the proper driver
+       // String driverKey = ecordDriverConfig.manufacturer() + "-" + ecordDriverConfig.hwVersion() + "-" +
+         //       ecordDriverConfig.swVersion();
+        String driverKey = "onLab-1.0.0-1.0.0";
+        SparseAnnotations annotations = DefaultAnnotations.builder()
+                .set(NO_LLDP, "any")
+                .set(DOMAIN_ID, domainId.id())
+                .set(DRIVER, driverKey)
+                .build();
+
+        DeviceDescription deviceDescription = new DefaultDeviceDescription(
+                deviceId.uri(),
+                Device.Type.SWITCH,
+                "onLab", "1.0.0",
+                "1.0.0", UNKNOWN,
+                chassisId,
+                annotations);
+        deviceProviderService.deviceConnected(deviceId, deviceDescription);
+    }
+
+    /**
+     * Notify the core system of all ports of a domain device.
+     * @param deviceId device identifier
+     * @param portDescriptions description of ports
+     */
+    private void advertiseDevicePorts(DeviceId deviceId, List<PortDescription> portDescriptions) {
+        log.info("Notifying ecord virtual ports...");
+        deviceProviderService.updatePorts(deviceId, portDescriptions);
+       // addGlobalMefLtp(deviceId, portDescriptions);
+        notifyXoS(deviceId, portDescriptions);
+    }
+
+    private void disconnectDevice(DeviceId deviceId) {
+        deviceProviderService.deviceDisconnected(deviceId);
+    }
+
+    private void notifyXoS(DeviceId deviceId, List<PortDescription> portDescriptions) {
+        portDescriptions.forEach(port -> {
+            if (port.annotations().keys().contains(MEF_IF_TYPE)) {
+                Type type = Type.valueOf(
+                        port.annotations().value(MEF_IF_TYPE));
+                ConnectPoint cp = new ConnectPoint(deviceId, port.portNumber());
+                switch (type) {
+                    case UNI:
+                        ObjectNode body = xoSHttpClient.mapper().createObjectNode();
+                        body.put("tenant", port.annotations().value(DOMAIN_ID));
+                        body.put("name", "UNI:" + cp.toString());
+                        body.put("latlng", "[" + port.annotations().value(LATITUDE) + "," +
+                                port.annotations().value(LONGITUDE) + "]");
+                        body.put("cpe_id", cp.toString());
+                        xoSHttpClient.restPut(body.toString(), cp.toString());
+                        break;
+                    case ENNI:
+
+                        break;
+                    case INNI:
+
+                        break;
+                    case GENERIC:
+
+                        break;
+                    default:
+                        return;
+                }
+            }
+        });
+    }
+
+    /**
+     * Adds global {@link CarrierEthernetLogicalTerminationPoint}
+     * to {@link MetroOrchestrationService}.
+     *
+     * @param deviceId subject device ID
+     * @param portDescriptions list of notified ports
+     */
+    private void addGlobalMefLtp(DeviceId deviceId, List<PortDescription> portDescriptions) {
+        portDescriptions.forEach(port -> {
+            if (port.annotations().keys().contains(MEF_IF_TYPE)) {
+                Type type = Type.valueOf(
+                        port.annotations().value(MEF_IF_TYPE));
+                CarrierEthernetNetworkInterface ni;
+                ConnectPoint cp = new ConnectPoint(deviceId, port.portNumber());
+                switch (type) {
+                    case UNI:
+                        ni = CarrierEthernetUni.builder()
+                                .cp(cp)
+                                .annotations(port.annotations())
+                                .build();
+                        break;
+                    case ENNI:
+                        ni = CarrierEthernetEnni.builder()
+                                .cp(cp)
+                                .build();
+                        break;
+                    case INNI:
+                        ni = CarrierEthernetInni.builder()
+                                .cp(cp)
+                                .annotations(port.annotations())
+                                .build();
+                        break;
+                    case GENERIC:
+                        ni = new CarrierEthernetGenericNi(cp, null, port.annotations());
+                        break;
+                    default:
+                            return;
+                }
+                metroOrchestrationService.addGlobalLtp(
+                        new CarrierEthernetLogicalTerminationPoint(null, ni));
+            }
+        });
+    }
+
+    private void readXoSEndPointConfig() {
+        xoSHttpClient =
+                new XoSHttpClient(configRegistry.getConfig(appId, XosEndPointConfig.class).xos());
+    }
+
+    private void readDriverFromConfig() {
+        ecordDriverConfig =
+                configRegistry.getConfig(appId, EcordDriverConfig.class);
+    }
+
+    private class InternalConfigListener implements NetworkConfigListener {
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if (!event.configClass().equals(EcordDriverConfig.class) ||
+                    !event.configClass().equals(XosEndPointConfig.class)) {
+                return;
+            }
+            switch (event.type()) {
+                case CONFIG_ADDED:
+                    log.info("Network configuration added");
+                    if (event.configClass().equals(EcordDriverConfig.class)) {
+                        eventExecutor.execute(VirtualDomainDeviceProvider.this::readDriverFromConfig);
+                    } else {
+                        eventExecutor.execute(VirtualDomainDeviceProvider.this::readXoSEndPointConfig);
+                    }
+
+                    break;
+                case CONFIG_UPDATED:
+                    log.info("Network configuration updated");
+                    if (event.configClass().equals(EcordDriverConfig.class)) {
+                        eventExecutor.execute(VirtualDomainDeviceProvider.this::readDriverFromConfig);
+                    } else {
+                        eventExecutor.execute(VirtualDomainDeviceProvider.this::readXoSEndPointConfig);
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/VirtualLinkProvider.java b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/VirtualLinkProvider.java
new file mode 100644
index 0000000..361d74c
--- /dev/null
+++ b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/VirtualLinkProvider.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.ce.global.virtualdomain;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.Port;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkDescription;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.link.LinkListener;
+import org.onosproject.net.link.LinkProvider;
+import org.onosproject.net.link.LinkProviderRegistry;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.provider.ProviderId;
+import org.opencord.ce.api.services.virtualprovider.LinkId;
+import org.slf4j.Logger;
+
+import java.util.Map;
+
+import static org.onosproject.net.Link.Type.INDIRECT;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.opencord.ce.global.virtualdomain.VirtualDomainDeviceProvider.PROVIDER_NAME;
+
+/**
+ * Domain interlinks provider.
+ */
+@Component(immediate = true)
+public class VirtualLinkProvider implements LinkProvider {
+    public static final ProviderId PROVIDER_ID = new ProviderId("domain", PROVIDER_NAME);
+    public static final String INTERLINK_ID = "interlinkId";
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkProviderRegistry linkProviderRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkService linkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    private final Map<LinkId, ConnectPoint> stagedInterlinks = Maps.newConcurrentMap();
+    private final Map<LinkId, Pair<ConnectPoint, ConnectPoint>>
+            committedInterlinks = Maps.newConcurrentMap();
+    private final DeviceListener deviceListener = new InternalDeviceListener();
+    private final LinkListener linkListener = new InternalLinkListener();
+
+    protected LinkProviderService linkProviderService;
+
+    @Activate
+    public void activate() {
+        coreService.registerApplication(PROVIDER_NAME);
+        linkProviderService = linkProviderRegistry.register(this);
+        deviceService.addListener(deviceListener);
+        linkService.addListener(linkListener);
+        log.info("Started");
+
+    }
+
+    @Deactivate
+    public void deactivate() {
+        linkProviderRegistry.unregister(this);
+        deviceService.removeListener(deviceListener);
+        linkService.removeListener(linkListener);
+        log.info("Stopped");
+    }
+
+
+    @Override
+    public ProviderId id() {
+        return PROVIDER_ID;
+    }
+
+    private void addInterLink(ConnectPoint point1, ConnectPoint point2, LinkId linkId) {
+        SparseAnnotations annotations = DefaultAnnotations.builder()
+                .set(INTERLINK_ID, linkId.id())
+                .build();
+        LinkDescription linkDescription = new DefaultLinkDescription(point1, point2,
+                INDIRECT, true, annotations);
+        linkProviderService.linkDetected(linkDescription);
+    }
+
+    private class InternalLinkListener implements LinkListener {
+
+        @Override
+        public void event(LinkEvent event) {
+            Link link = event.subject();
+            switch (event.type()) {
+                case LINK_REMOVED:
+                   if (link.annotations().keys().contains(INTERLINK_ID)) {
+                       committedInterlinks.remove(LinkId.linkId(
+                               link.annotations().value(INTERLINK_ID)
+                       ));
+                   }
+                case LINK_ADDED:
+                    // not relevant for now
+                default:
+            }
+        }
+    }
+
+
+    private class InternalDeviceListener implements DeviceListener {
+        @Override
+        public boolean isRelevant(DeviceEvent event) {
+            return (event.type().equals(DeviceEvent.Type.PORT_ADDED) ||
+                    event.type().equals(DeviceEvent.Type.PORT_REMOVED) ||
+                    event.type().equals(DeviceEvent.Type.PORT_UPDATED)) &&
+                    event.port().annotations().keys().contains(INTERLINK_ID);
+        }
+
+        @Override
+        public void event(DeviceEvent event) {
+
+            Port port = event.port();
+            DeviceId subjectDeviceId = event.subject().id();
+            LinkId linkId = LinkId.linkId(port.annotations().value(INTERLINK_ID));
+
+            switch (event.type()) {
+                case PORT_ADDED:
+                case PORT_UPDATED:
+                    if (committedInterlinks.containsKey(linkId)) {
+                        // !!!! ===== !!!! //
+                        // committed interlinks cannot be subject to changes
+                        // unless a port is removed.
+                        // Possibly this is an error condition
+                        log.info("notified interlinks cannot be subject to changes," +
+                                "unless a port of the link is removed. Possibly this is" +
+                                "an error condition");
+                        return;
+                    } else {
+                        log.info("Adding virtual inter-links...");
+                        if (stagedInterlinks.containsKey(linkId)) {
+                            ConnectPoint point1 =
+                                    stagedInterlinks.get(linkId);
+                            ConnectPoint point2 =
+                                    new ConnectPoint(subjectDeviceId, port.number());
+                            if (subjectDeviceId.equals(point1.deviceId())) {
+                                return;
+                            }
+
+                            committedInterlinks.put(linkId, Pair.of(point1, point2));
+                            stagedInterlinks.remove(linkId);
+
+                            // only the master instance of the destination link can notify the link
+                            if (mastershipService.getLocalRole(point2.deviceId())
+                                    .equals(MastershipRole.MASTER)) {
+                                addInterLink(point1, point2, linkId);
+                            }
+                            if (mastershipService.getLocalRole(point1.deviceId())
+                                    .equals(MastershipRole.MASTER)) {
+                                addInterLink(point2, point1, linkId);
+                            }
+                        } else {
+                            stagedInterlinks.put(linkId,
+                                    new ConnectPoint(subjectDeviceId, port.number()));
+                        }
+                    }
+                    break;
+                case PORT_REMOVED:
+                    if (committedInterlinks.containsKey(linkId)) {
+                        Pair<ConnectPoint, ConnectPoint> points =
+                                committedInterlinks.get(linkId);
+                        committedInterlinks.remove(linkId);
+                        if (points.getLeft().deviceId().equals(subjectDeviceId)) {
+                            linkProviderService.linksVanished(points.getLeft());
+                            stagedInterlinks.put(linkId, points.getRight());
+                        } else {
+                            linkProviderService.linksVanished(points.getRight());
+                            stagedInterlinks.put(linkId, points.getLeft());
+                        }
+                    } else if (stagedInterlinks.containsKey(linkId)) {
+                        stagedInterlinks.remove(linkId);
+                    }
+                    break;
+                default:
+            }
+        }
+    }
+}
diff --git a/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/XoSHttpClient.java b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/XoSHttpClient.java
new file mode 100644
index 0000000..1d72b40
--- /dev/null
+++ b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/XoSHttpClient.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.ce.global.virtualdomain;
+
+import com.fasterxml.jackson.databind.SerializationFeature;
+import org.glassfish.jersey.client.ClientProperties;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import static java.net.HttpURLConnection.HTTP_OK;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.opencord.ce.global.virtualdomain.config.XosEndPointConfig.XosEndPoint;
+
+/**
+ * Implementation of REST client for XOS Metro-net service.
+ */
+public class XoSHttpClient extends AbstractWebResource {
+    protected final Logger log = getLogger(getClass());
+
+    private static final int DEFAULT_TIMEOUT_MS = 2000;
+
+    private XosEndPoint xosEndPoint;
+    private final Client client = ClientBuilder.newClient();
+
+
+    public XoSHttpClient(XosEndPoint xosEndPoint) {
+        this.xosEndPoint = xosEndPoint;
+
+        client.property(ClientProperties.CONNECT_TIMEOUT, DEFAULT_TIMEOUT_MS);
+        client.property(ClientProperties.READ_TIMEOUT, DEFAULT_TIMEOUT_MS);
+        mapper().enable(SerializationFeature.INDENT_OUTPUT);
+    }
+
+    public void restPut(String body, String cpeId) {
+        WebTarget webTarget = client.target("http://" + xosEndPoint.ipAddress().toString() +
+                 xosEndPoint.resource() + "/" + cpeId);
+        Response response;
+        response = webTarget.request(MediaType.APPLICATION_JSON)
+                .put(Entity.entity(body, MediaType.APPLICATION_JSON));
+        try {
+            if (response.getStatus() != HTTP_OK) {
+                log.warn("Failed to put resource {}", xosEndPoint.resource());
+            }
+        } catch (javax.ws.rs.ProcessingException e) {
+            log.error("Javax process exception in XoSClientHttp: {}", e.getMessage());
+        }
+    }
+}
diff --git a/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/EcordDriverConfig.java b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/EcordDriverConfig.java
new file mode 100644
index 0000000..1077993
--- /dev/null
+++ b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/EcordDriverConfig.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.ce.global.virtualdomain.config;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+
+/**
+ * ICONA driver configuration class.
+ */
+public class EcordDriverConfig extends Config<ApplicationId> {
+
+    private static final String MANUFACTURER = "manufacturer";
+    private static final String SW_VERSION = "swVersion";
+    private static final String HW_VERSION = "hwVersion";
+
+
+    public String manufacturer() {
+        return object.get(MANUFACTURER).asText();
+    }
+
+    public String swVersion() {
+        return object.get(SW_VERSION).asText();
+    }
+
+    public String hwVersion() {
+        return  object.get(HW_VERSION).asText();
+    }
+
+}
diff --git a/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/XosEndPointConfig.java b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/XosEndPointConfig.java
new file mode 100644
index 0000000..df0c59e
--- /dev/null
+++ b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/XosEndPointConfig.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.opencord.ce.global.virtualdomain.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+
+/**
+ * Global XoS endpoint.
+ */
+public class XosEndPointConfig extends Config<ApplicationId> {
+    private static final String USERNAME = "username";
+    private static final String PASSWD = "password";
+    private static final String XOS = "xos";
+    private static final String ADDRESS = "address";
+    private static final String RESOURCE = "resource";
+
+    public XosEndPoint xos() {
+        JsonNode xosNode = object.path(XOS);
+        IpAddress ipAddress = IpAddress.valueOf(xosNode.get(ADDRESS).asText());
+        String username = xosNode.get(USERNAME).asText();
+        String password = xosNode.get(PASSWD).asText();
+        String resource = xosNode.get(RESOURCE).asText();
+        return new XosEndPoint(ipAddress, username, password, resource);
+    }
+
+    public static class XosEndPoint {
+        private IpAddress ipAddress;
+        private String username;
+        private String password;
+        private String resource;
+
+        public XosEndPoint(IpAddress ipAddress, String username,
+                           String password, String resource) {
+            this.ipAddress = ipAddress;
+            this.username = username;
+            this.password = password;
+            this.resource = resource;
+        }
+
+        public IpAddress ipAddress() {
+            return ipAddress;
+        }
+
+        public String username() {
+            return username;
+        }
+
+        public String password() {
+            return password;
+        }
+
+        public String resource() {
+            return resource;
+        }
+    }
+
+}
diff --git a/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/package-info.java b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/package-info.java
new file mode 100644
index 0000000..62fbacc
--- /dev/null
+++ b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/config/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Configuration classes.
+ */
+package org.opencord.ce.global.virtualdomain.config;
\ No newline at end of file
diff --git a/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/package-info.java b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/package-info.java
new file mode 100644
index 0000000..c71407e
--- /dev/null
+++ b/global/virtualprovider/src/main/java/org/opencord/ce/global/virtualdomain/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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 for domain topology provider.
+ */
+package org.opencord.ce.global.virtualdomain;
\ No newline at end of file