CORD-506 Apply existing service dependency when VM is detected

Change-Id: Ib0872c823347bfb6091d6c5f872657f10b7b1083
diff --git a/app.xml b/app.xml
index 5163375..7e8f409 100644
--- a/app.xml
+++ b/app.xml
@@ -18,7 +18,7 @@
         category="Traffic Steering" url="http://onosproject.org" title="CORD Virtual Tenant Network"
         featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
         features="${project.artifactId}"
-        apps="org.onosproject.ovsdb-base,org.onosproject.dhcp">
+        apps="org.onosproject.ovsdb-base,org.onosproject.dhcp,org.onosproject.xosclient">
     <description>${project.description}</description>
     <artifact>mvn:${project.groupId}/onos-app-cordvtn/${project.version}</artifact>
 </app>
diff --git a/pom.xml b/pom.xml
index 64e6e2f..705a9b4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,6 +103,11 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-xos-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>com.jcraft</groupId>
             <artifactId>jsch</artifactId>
             <version>0.1.53</version>
diff --git a/src/main/java/org/onosproject/cordvtn/api/CordService.java b/src/main/java/org/onosproject/cordvtn/api/CordService.java
index 8a10015..be2b810 100644
--- a/src/main/java/org/onosproject/cordvtn/api/CordService.java
+++ b/src/main/java/org/onosproject/cordvtn/api/CordService.java
@@ -43,6 +43,7 @@
     private final IpAddress serviceIp;
     private final Map<Host, IpAddress> hosts;
     private final Set<CordServiceId> tenantServices;
+    private final Set<CordServiceId> providerServices;
 
     /**
      * Default constructor.
@@ -51,9 +52,11 @@
      * @param osSubnet OpenStack subnet
      * @param hosts host and tunnel ip map
      * @param tenantServices list of tenant service ids
+     * @param providerServices list of provider service ids
      */
     public CordService(Network osNet, Subnet osSubnet,
-                       Map<Host, IpAddress> hosts, Set<CordServiceId> tenantServices) {
+                       Map<Host, IpAddress> hosts, Set<CordServiceId> tenantServices,
+                       Set<CordServiceId> providerServices) {
         this.id = CordServiceId.of(osNet.getId());
         this.segmentationId = Long.parseLong(osNet.getProviderSegID());
         this.serviceType = getServiceType(osNet.getName());
@@ -61,6 +64,7 @@
         this.serviceIp = IpAddress.valueOf(osSubnet.getGateway());
         this.hosts = hosts;
         this.tenantServices = tenantServices;
+        this.providerServices = providerServices;
     }
 
     /**
@@ -126,6 +130,15 @@
         return tenantServices;
     }
 
+    /**
+     * Returns provider service IDs.
+     *
+     * @return list of provider service id
+     */
+    public Set<CordServiceId> providerServices() {
+        return providerServices;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(id);
@@ -152,6 +165,7 @@
                 .add("serviceIpRange", serviceIpRange)
                 .add("serviceIp", serviceIp)
                 .add("tenantServices", tenantServices)
+                .add("providerServices", providerServices)
                 .toString();
     }
 
@@ -162,7 +176,7 @@
      * @param netName network name
      * @return network type, or PRIVATE if it doesn't match any type
      */
-    private ServiceType getServiceType(String netName) {
+    public static ServiceType getServiceType(String netName) {
         checkNotNull(netName);
 
         String name = netName.toUpperCase();
diff --git a/src/main/java/org/onosproject/cordvtn/api/CordVtnConfig.java b/src/main/java/org/onosproject/cordvtn/api/CordVtnConfig.java
index 2c48ba3..9f3a279 100644
--- a/src/main/java/org/onosproject/cordvtn/api/CordVtnConfig.java
+++ b/src/main/java/org/onosproject/cordvtn/api/CordVtnConfig.java
@@ -25,6 +25,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.config.Config;
+import org.onosproject.xosclient.api.XosAccess;
 import org.slf4j.Logger;
 
 import java.util.Map;
@@ -59,10 +60,12 @@
     public static final String SSH_KEY_FILE = "sshKeyFile";
 
     public static final String OPENSTACK = "openstack";
-    public static final String OPENSTACK_ENDPOINT = "endpoint";
-    public static final String OPENSTACK_TENANT = "tenant";
-    public static final String OPENSTACK_USER = "user";
-    public static final String OPENSTACK_PASSWORD = "password";
+    public static final String XOS = "xos";
+
+    public static final String ENDPOINT = "endpoint";
+    public static final String TENANT = "tenant";
+    public static final String USER = "user";
+    public static final String PASSWORD = "password";
 
     /**
      * Returns the set of nodes read from network config.
@@ -183,6 +186,28 @@
     }
 
     /**
+     * Returns XOS access information.
+     *
+     * @return XOS access, or null
+     */
+    public XosAccess xosAccess() {
+        JsonNode jsonNode = object.get(XOS);
+        if (jsonNode == null) {
+            log.error("Failed to get XOS configurations");
+            return null;
+        }
+
+        try {
+            return new XosAccess(getConfig(jsonNode, ENDPOINT),
+                                 getConfig(jsonNode, USER),
+                                 getConfig(jsonNode, PASSWORD));
+        } catch (NullPointerException e) {
+            log.error("Failed to get XOS access");
+            return null;
+        }
+    }
+
+    /**
      * Returns OpenStack API access information.
      *
      * @return openstack config
@@ -196,10 +221,10 @@
 
         try {
             return new OpenStackConfig(
-                    jsonNode.path(OPENSTACK_ENDPOINT).asText(),
-                    jsonNode.path(OPENSTACK_TENANT).asText(),
-                    jsonNode.path(OPENSTACK_USER).asText(),
-                    jsonNode.path(OPENSTACK_PASSWORD).asText());
+                    jsonNode.path(ENDPOINT).asText(),
+                    jsonNode.path(TENANT).asText(),
+                    jsonNode.path(USER).asText(),
+                    jsonNode.path(PASSWORD).asText());
         } catch (IllegalArgumentException | NullPointerException e) {
             log.error("Failed to get OpenStack configurations");
             return null;
diff --git a/src/main/java/org/onosproject/cordvtn/impl/CordVtn.java b/src/main/java/org/onosproject/cordvtn/impl/CordVtn.java
index 61f37b5..f7039c3 100644
--- a/src/main/java/org/onosproject/cordvtn/impl/CordVtn.java
+++ b/src/main/java/org/onosproject/cordvtn/impl/CordVtn.java
@@ -30,6 +30,7 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.cordvtn.api.CordService;
+import org.onosproject.cordvtn.api.CordService.ServiceType;
 import org.onosproject.cordvtn.api.CordServiceId;
 import org.onosproject.cordvtn.api.CordVtnConfig;
 import org.onosproject.cordvtn.api.CordVtnNode;
@@ -66,6 +67,8 @@
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
+import org.onosproject.xosclient.api.XosAccess;
+import org.onosproject.xosclient.api.XosClientService;
 import org.openstack4j.api.OSClient;
 import org.openstack4j.api.exceptions.AuthenticationException;
 import org.openstack4j.model.identity.Access;
@@ -85,6 +88,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.cordvtn.api.CordService.getServiceType;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -130,6 +134,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DhcpService dhcpService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected XosClientService xosClient;
+
     private final ConfigFactory configFactory =
             new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
                 @Override
@@ -412,23 +419,7 @@
             return null;
         }
 
-        // here it assumes all cord service networks has only one subnet
-        Subnet osSubnet = osNet.getNeutronSubnets().stream()
-                .findFirst()
-                .orElse(null);
-        if (osSubnet == null) {
-            log.warn("Couldn't find OpenStack subnet for service {}", serviceId.id());
-            return null;
-        }
-
-        Set<CordServiceId> tServices = Sets.newHashSet();
-        // TODO get tenant services from XOS
-
-        Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(osNet)
-                .stream()
-                .collect(Collectors.toMap(host -> host, this::getTunnelIp));
-
-        return new CordService(osNet, osSubnet, hosts, tServices);
+        return getCordService(osNet);
     }
 
     /**
@@ -450,14 +441,28 @@
             return null;
         }
 
-        Set<CordServiceId> tServices = Sets.newHashSet();
-        // TODO get tenant services from XOS
-
         Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(osNet)
                 .stream()
                 .collect(Collectors.toMap(host -> host, this::getTunnelIp));
 
-        return new CordService(osNet, osSubnet, hosts, tServices);
+        ServiceType serviceType = getServiceType(osNet.getName());
+        // allows working without XOS for now
+        Set<CordServiceId> tServices = Sets.newHashSet();
+        Set<CordServiceId> pServices = Sets.newHashSet();
+
+        if (xosClient.access() != null && serviceType != ServiceType.MANAGEMENT) {
+            tServices = xosClient.vtnServiceApi().getTenantServices(serviceId.id())
+                    .stream()
+                    .map(CordServiceId::of)
+                    .collect(Collectors.toSet());
+
+            pServices = xosClient.vtnServiceApi().getProviderServices(serviceId.id())
+                    .stream()
+                    .map(CordServiceId::of)
+                    .collect(Collectors.toSet());
+        }
+
+        return new CordService(osNet, osSubnet, hosts, tServices, pServices);
     }
 
     /**
@@ -576,6 +581,7 @@
 
         CordService service = getCordService(osNet);
         if (service == null) {
+            log.warn("Failed to get CordService for {}", osNet.getName());
             return;
         }
 
@@ -587,7 +593,12 @@
                 arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
             case PUBLIC:
             default:
-                // TODO check if the service needs an update on its group buckets after done CORD-433
+                // TODO get bidirectional information from XOS once XOS supports
+                service.tenantServices().stream().forEach(
+                        tServiceId -> createServiceDependency(tServiceId, service.id(), true));
+                service.providerServices().stream().forEach(
+                        pServiceId -> createServiceDependency(service.id(), pServiceId, true));
+
                 ruleInstaller.updateServiceGroup(service);
                 // sends gratuitous ARP here for the case of adding existing VMs
                 // when ONOS or cordvtn app is restarted
@@ -648,8 +659,9 @@
                 }
             case PUBLIC:
             default:
-                // TODO check if the service needs an update on its group buckets after done CORD-433
-                ruleInstaller.updateServiceGroup(service);
+                if (!service.tenantServices().isEmpty()) {
+                    ruleInstaller.updateServiceGroup(service);
+                }
                 break;
         }
     }
@@ -717,6 +729,8 @@
      * @param newMac mac address to update
      */
     private void setPrivateGatewayMac(MacAddress newMac) {
+        checkNotNull(osAccess, "OpenStack access is not set");
+
         if (newMac == null || newMac.equals(privateGatewayMac)) {
             // no updates, do nothing
             return;
@@ -725,13 +739,11 @@
         privateGatewayMac = newMac;
         log.debug("Set service gateway MAC address to {}", privateGatewayMac.toString());
 
-        // TODO get existing service list from XOS and replace the loop below
-        Set<String> vNets = Sets.newHashSet();
-        hostService.getHosts().forEach(host -> vNets.add(host.annotations().value(SERVICE_ID)));
-        vNets.remove(null);
+        OSClient osClient = OSFactory.clientFromAccess(osAccess);
+        List<Network> vNets = Lists.newArrayList(osClient.networking().network().list().iterator());
 
         vNets.stream().forEach(vNet -> {
-            CordService service = getCordService(CordServiceId.of(vNet));
+            CordService service = getCordService(vNet);
             if (service != null) {
                 arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
                 arpProxy.sendGratuitousArpForGateway(service.serviceIp(), service.hosts().keySet());
@@ -763,6 +775,8 @@
      * @param osConfig openstack config
      */
     private void setOpenstackAccess(CordVtnConfig.OpenStackConfig osConfig) {
+        checkNotNull(osConfig, "OpenStack access is not configured");
+
         log.debug("Get OpenStack access with Endpoint: {} Tenant: {} User: {} Passwd: {}",
                   osConfig.endpoint(),
                   osConfig.tenant(),
@@ -781,6 +795,22 @@
     }
 
     /**
+     * Sets XOS access information.
+     *
+     * @param xosAccess xos access
+     */
+    private void setXosAccess(XosAccess xosAccess) {
+        checkNotNull(xosAccess, "XOS access is not configured");
+
+        log.debug("Set XOS access with Endpoint: {} User: {} Passwd: {}",
+                  xosAccess.endpoint(),
+                  xosAccess.username(),
+                  xosAccess.password());
+
+        xosClient.setAccess(xosAccess);
+    }
+
+    /**
      * Updates configurations.
      */
     private void readConfiguration() {
@@ -790,6 +820,7 @@
             return;
         }
 
+        setXosAccess(config.xosAccess());
         setOpenstackAccess(config.openstackConfig());
         setPrivateGatewayMac(config.privateGatewayMac());
         setPublicGatewayMac(config.publicGateways());
diff --git a/src/main/java/org/onosproject/cordvtn/impl/CordVtnRuleInstaller.java b/src/main/java/org/onosproject/cordvtn/impl/CordVtnRuleInstaller.java
index 5914b71..823a5b1 100644
--- a/src/main/java/org/onosproject/cordvtn/impl/CordVtnRuleInstaller.java
+++ b/src/main/java/org/onosproject/cordvtn/impl/CordVtnRuleInstaller.java
@@ -282,12 +282,12 @@
             GroupId groupId = createServiceGroup(deviceId, pService);
             outGroups.put(deviceId, groupId);
 
-            Set<PortNumber> vms = tService.hosts().keySet()
+            Set<PortNumber> tServiceVms = tService.hosts().keySet()
                     .stream()
                     .filter(host -> host.location().deviceId().equals(deviceId))
                     .map(host -> host.location().port())
                     .collect(Collectors.toSet());
-            inPorts.put(deviceId, vms);
+            inPorts.put(deviceId, tServiceVms);
         });
 
         populateIndirectAccessRule(srcRange, serviceIp, outGroups);