Inject service FQDN into kubevirt node, let onos resolve service IP

Change-Id: I6f3f66ad97f8be461c464dad5d133f3477c40f5a
diff --git a/apps/kubevirt-node/BUILD b/apps/kubevirt-node/BUILD
index 4b6bc0f..3cf4721 100644
--- a/apps/kubevirt-node/BUILD
+++ b/apps/kubevirt-node/BUILD
@@ -30,6 +30,7 @@
     "@jackson_dataformat_yaml//jar",
     "@jackson_datatype_jsr310//jar",
     "@snakeyaml//jar",
+    "@dns_java//jar",
 ]
 
 onos_app(
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtApiConfig.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtApiConfig.java
index 1fa1fd9..c4e2bb8 100644
--- a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtApiConfig.java
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtApiConfig.java
@@ -38,6 +38,7 @@
     private final String caCertData;
     private final String clientCertData;
     private final String clientKeyData;
+    private final String serviceFqdn;
 
     /**
      * Default constructor for Kubevirt API config.
@@ -50,11 +51,13 @@
      * @param caCertData        CA certificate data
      * @param clientCertData    client certificate data
      * @param clientKeyData     client key data
+     * @param serviceFqdn       service FQDN
      */
     private DefaultKubevirtApiConfig(Scheme scheme, IpAddress ipAddress,
                                     int port, State state,
                                     String token, String caCertData,
-                                    String clientCertData, String clientKeyData) {
+                                    String clientCertData, String clientKeyData,
+                                     String serviceFqdn) {
         this.scheme = scheme;
         this.ipAddress = ipAddress;
         this.port = port;
@@ -63,6 +66,7 @@
         this.caCertData = caCertData;
         this.clientCertData = clientCertData;
         this.clientKeyData = clientKeyData;
+        this.serviceFqdn = serviceFqdn;
     }
 
     @Override
@@ -96,6 +100,7 @@
                 .caCertData(caCertData)
                 .clientCertData(clientCertData)
                 .clientKeyData(clientKeyData)
+                .serviceFqdn(serviceFqdn)
                 .build();
     }
 
@@ -120,6 +125,11 @@
     }
 
     @Override
+    public String serviceFqdn() {
+        return serviceFqdn;
+    }
+
+    @Override
     public boolean equals(Object o) {
         if (this == o) {
             return true;
@@ -132,13 +142,14 @@
                 ipAddress.equals(that.ipAddress) && state == that.state &&
                 token.equals(that.token) && caCertData.equals(that.caCertData) &&
                 clientCertData.equals(that.clientCertData) &&
-                clientKeyData.equals(that.clientKeyData);
+                clientKeyData.equals(that.clientKeyData) &&
+                Objects.equals(serviceFqdn, that.serviceFqdn);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(scheme, ipAddress, port, state, token,
-                caCertData, clientCertData, clientKeyData);
+                caCertData, clientCertData, clientKeyData, serviceFqdn);
     }
 
     @Override
@@ -152,6 +163,7 @@
                 .add("caCertData", caCertData)
                 .add("clientCertData", clientCertData)
                 .add("clientKeyData", clientKeyData)
+                .add("serviceFqdn", serviceFqdn)
                 .toString();
     }
 
@@ -174,6 +186,7 @@
         private String caCertData;
         private String clientCertData;
         private String clientKeyData;
+        private String serviceFqdn;
 
         @Override
         public KubevirtApiConfig build() {
@@ -188,55 +201,61 @@
             }
 
             return new DefaultKubevirtApiConfig(scheme, ipAddress, port, state,
-                    token, caCertData, clientCertData, clientKeyData);
+                    token, caCertData, clientCertData, clientKeyData, serviceFqdn);
         }
 
         @Override
-        public KubevirtApiConfig.Builder scheme(Scheme scheme) {
+        public Builder scheme(Scheme scheme) {
             this.scheme = scheme;
             return this;
         }
 
         @Override
-        public KubevirtApiConfig.Builder ipAddress(IpAddress ipAddress) {
+        public Builder ipAddress(IpAddress ipAddress) {
             this.ipAddress = ipAddress;
             return this;
         }
 
         @Override
-        public KubevirtApiConfig.Builder port(int port) {
+        public Builder port(int port) {
             this.port = port;
             return this;
         }
 
         @Override
-        public KubevirtApiConfig.Builder state(State state) {
+        public Builder state(State state) {
             this.state = state;
             return this;
         }
 
         @Override
-        public KubevirtApiConfig.Builder token(String token) {
+        public Builder token(String token) {
             this.token = token;
             return this;
         }
 
         @Override
-        public KubevirtApiConfig.Builder caCertData(String caCertData) {
+        public Builder caCertData(String caCertData) {
             this.caCertData = caCertData;
             return this;
         }
 
         @Override
-        public KubevirtApiConfig.Builder clientCertData(String clientCertData) {
+        public Builder clientCertData(String clientCertData) {
             this.clientCertData = clientCertData;
             return this;
         }
 
         @Override
-        public KubevirtApiConfig.Builder clientKeyData(String clientKeyData) {
+        public Builder clientKeyData(String clientKeyData) {
             this.clientKeyData = clientKeyData;
             return this;
         }
+
+        @Override
+        public Builder serviceFqdn(String serviceFqdn) {
+            this.serviceFqdn = serviceFqdn;
+            return this;
+        }
     }
 }
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtApiConfig.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtApiConfig.java
index e33e4b5..4cd03af 100644
--- a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtApiConfig.java
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtApiConfig.java
@@ -117,6 +117,13 @@
     String clientKeyData();
 
     /**
+     * Returns the Fully Qualified Domain Name (FQDN) of the service.
+     *
+     * @return service Fqdn
+     */
+    String serviceFqdn();
+
+    /**
      * Builder of new API config entity.
      */
     interface Builder {
@@ -191,5 +198,13 @@
          * @return KubeVirt API config builder
          */
         Builder clientKeyData(String clientKeyData);
+
+        /**
+         * Returns KUbevirt API server config builder with supplied service Fqdn.
+         *
+         * @param serviceFqdn service FQDN
+         * @return KubeVirt API config builder
+         */
+        Builder serviceFqdn(String serviceFqdn);
     }
 }
diff --git a/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtApiConfigTest.java b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtApiConfigTest.java
index 4aa3d87..910ff09 100644
--- a/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtApiConfigTest.java
+++ b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtApiConfigTest.java
@@ -53,6 +53,9 @@
     private static final String CLIENT_KEY_DATA_1 = "clientKeyData1";
     private static final String CLIENT_KEY_DATA_2 = "clientKeyData2";
 
+    private static final String SERVICE_FQDN_1 = "kubevirt.edgestack.svc.cluster.local";
+    private static final String SERVICE_FQDN_2 = "sona.edgestack.svc.cluster.local";
+
     private KubevirtApiConfig config1;
     private KubevirtApiConfig sameAsConfig1;
     private KubevirtApiConfig config2;
@@ -79,6 +82,7 @@
                 .caCertData(CA_CERT_DATA_1)
                 .clientCertData(CLIENT_CERT_DATA_1)
                 .clientKeyData(CLIENT_KEY_DATA_1)
+                .serviceFqdn(SERVICE_FQDN_1)
                 .build();
 
         sameAsConfig1 = DefaultKubevirtApiConfig.builder()
@@ -90,6 +94,7 @@
                 .caCertData(CA_CERT_DATA_1)
                 .clientCertData(CLIENT_CERT_DATA_1)
                 .clientKeyData(CLIENT_KEY_DATA_1)
+                .serviceFqdn(SERVICE_FQDN_1)
                 .build();
 
         config2 = DefaultKubevirtApiConfig.builder()
@@ -101,6 +106,7 @@
                 .caCertData(CA_CERT_DATA_2)
                 .clientCertData(CLIENT_CERT_DATA_2)
                 .clientKeyData(CLIENT_KEY_DATA_2)
+                .serviceFqdn(SERVICE_FQDN_2)
                 .build();
     }
 
@@ -129,5 +135,6 @@
         assertEquals(CA_CERT_DATA_1, config.caCertData());
         assertEquals(CLIENT_CERT_DATA_1, config.clientCertData());
         assertEquals(CLIENT_KEY_DATA_1, config.clientKeyData());
+        assertEquals(SERVICE_FQDN_1, config.serviceFqdn());
     }
 }
diff --git a/apps/kubevirt-node/app/BUILD b/apps/kubevirt-node/app/BUILD
index fbc201a..fcab1fb 100644
--- a/apps/kubevirt-node/app/BUILD
+++ b/apps/kubevirt-node/app/BUILD
@@ -32,6 +32,7 @@
     "@jackson_datatype_jsr310//jar",
     "@snakeyaml//jar",
     "@commons_net//jar",
+    "@dns_java//jar",
 ]
 
 TEST_DEPS = TEST_ADAPTERS + TEST_REST + [
diff --git a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigCodec.java b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigCodec.java
index 3c09605..84a17e5 100644
--- a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigCodec.java
+++ b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigCodec.java
@@ -41,6 +41,7 @@
     private static final String CA_CERT_DATA = "caCertData";
     private static final String CLIENT_CERT_DATA = "clientCertData";
     private static final String CLIENT_KEY_DATA = "clientKeyData";
+    private static final String SERVICE_FQDN = "serviceFqdn";
 
     private static final String MISSING_MESSAGE = " is required in KubevirtApiConfig";
 
@@ -79,6 +80,10 @@
             }
         }
 
+        if (entity.serviceFqdn() != null) {
+            node.put(SERVICE_FQDN, entity.serviceFqdn());
+        }
+
         return node;
     }
 
@@ -104,6 +109,7 @@
         JsonNode caCertDataJson = json.get(CA_CERT_DATA);
         JsonNode clientCertDataJson = json.get(CLIENT_CERT_DATA);
         JsonNode clientKeyDataJson = json.get(CLIENT_KEY_DATA);
+        JsonNode serviceFqdn = json.get(SERVICE_FQDN);
 
         String token = "";
         String caCertData = "";
@@ -156,6 +162,10 @@
             builder.clientKeyData(clientKeyData);
         }
 
+        if (serviceFqdn != null) {
+            builder.serviceFqdn(serviceFqdn.asText());
+        }
+
         return builder.build();
     }
 }
diff --git a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtNodeHandler.java b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtNodeHandler.java
index b9c0880..86fa27d 100644
--- a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtNodeHandler.java
+++ b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtNodeHandler.java
@@ -99,6 +99,7 @@
 import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.getBooleanProperty;
 import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.getOvsdbClient;
 import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.isOvsdbConnected;
+import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.resolveHostname;
 import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.structurePortName;
 import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -123,6 +124,7 @@
     private static final long SLEEP_SHORT_MS = 1000; // we wait 1s
     private static final long SLEEP_MID_MS = 2000; // we wait 2s
     private static final long SLEEP_LONG_MS = 5000; // we wait 5s
+    private static final IpAddress DNS_SERVER_IP = IpAddress.valueOf("169.254.25.10");
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
@@ -298,7 +300,20 @@
     private void createBridge(KubevirtNode node, String bridgeName, DeviceId devId) {
         Device device = deviceService.getDevice(node.ovsdb());
 
-        IpAddress serverIp = apiConfigService.apiConfig().ipAddress();
+        IpAddress serverIp;
+        String serviceFqdn = apiConfigService.apiConfig().serviceFqdn();
+        IpAddress serviceIp = null;
+
+        if (serviceFqdn != null) {
+            serviceIp = resolveHostname(serviceFqdn);
+        }
+
+        if (serviceIp != null) {
+            serverIp = serviceIp;
+        } else {
+            serverIp = apiConfigService.apiConfig().ipAddress();
+        }
+
         ControllerInfo controlInfo = new ControllerInfo(serverIp, DEFAULT_OFPORT, DEFAULT_OF_PROTO);
         List<ControllerInfo> controllers = Lists.newArrayList(controlInfo);
 
diff --git a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/util/KubevirtNodeUtil.java b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/util/KubevirtNodeUtil.java
index a4b0abb..839695f 100644
--- a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/util/KubevirtNodeUtil.java
+++ b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/util/KubevirtNodeUtil.java
@@ -44,8 +44,11 @@
 import org.onosproject.ovsdb.controller.OvsdbNodeId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.xbill.DNS.Address;
 
 import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.Dictionary;
 import java.util.HashSet;
 import java.util.List;
@@ -82,6 +85,7 @@
     private static final String INTERFACE_KEY = "interface";
 
     private static final int PORT_NAME_MAX_LENGTH = 15;
+    private static final int DNS_DEFAULT_PORT = 53;
 
     /**
      * Prevents object installation from external.
@@ -385,4 +389,20 @@
                 .gatewayBridgeName(gatewayBridgeName)
                 .build();
     }
+
+    /**
+     * Resolve a DNS with the given DNS server and hostname.
+     *
+     * @param hostname      hostname to be resolved
+     * @return resolved IP address
+     */
+    public static IpAddress resolveHostname(String hostname) {
+        try {
+            InetAddress addr = Address.getByName(hostname);
+            return IpAddress.valueOf(IpAddress.Version.INET, addr.getAddress());
+        } catch (UnknownHostException e) {
+            log.warn("Failed to resolve IP address of host {}", hostname);
+        }
+        return null;
+    }
 }
diff --git a/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigCodecTest.java b/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigCodecTest.java
index 773401e..bd1c62b 100644
--- a/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigCodecTest.java
+++ b/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigCodecTest.java
@@ -86,6 +86,7 @@
                 .caCertData("caCertData")
                 .clientCertData("clientCertData")
                 .clientKeyData("clientKeyData")
+                .serviceFqdn("kubevirt.edgestack.svc.cluster.local")
                 .build();
         ObjectNode configJson = kubevirtApiConfigCodec.encode(config, context);
         assertThat(configJson, matchesKubevirtApiConfig(config));
@@ -107,6 +108,7 @@
         assertEquals("caCertData", config.caCertData());
         assertEquals("clientCertData", config.clientCertData());
         assertEquals("clientKeyData", config.clientKeyData());
+        assertEquals("kubevirt.edgestack.svc.cluster.local", config.serviceFqdn());
     }
 
     private KubevirtApiConfig getKubevirtApiConfig(String resourceName) throws IOException {
diff --git a/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigJsonMatcher.java b/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigJsonMatcher.java
index 17df9bc..1372d7d 100644
--- a/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigJsonMatcher.java
+++ b/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtApiConfigJsonMatcher.java
@@ -35,6 +35,7 @@
     private static final String CA_CERT_DATA = "caCertData";
     private static final String CLIENT_CERT_DATA = "clientCertData";
     private static final String CLIENT_KEY_DATA = "clientKeyData";
+    private static final String SERVICE_FQDN = "serviceFqdn";
 
     private KubevirtApiConfigJsonMatcher(KubevirtApiConfig kubevirtApiConfig) {
         this.kubevirtApiConfig = kubevirtApiConfig;
@@ -118,6 +119,17 @@
             }
         }
 
+        // service FQDN
+        JsonNode jsonServiceFqdn = jsonNode.get(SERVICE_FQDN);
+        String serviceFqdn = kubevirtApiConfig.serviceFqdn();
+
+        if (jsonServiceFqdn != null) {
+            if (!jsonServiceFqdn.asText().equals(serviceFqdn)) {
+                description.appendText("serviceFqdn was " + jsonServiceFqdn);
+                return false;
+            }
+        }
+
         return true;
     }
 
diff --git a/apps/kubevirt-node/app/src/test/resources/org/onosproject/kubevirtnode/codec/KubevirtApiConfig.json b/apps/kubevirt-node/app/src/test/resources/org/onosproject/kubevirtnode/codec/KubevirtApiConfig.json
index f706cc3..b42e618 100644
--- a/apps/kubevirt-node/app/src/test/resources/org/onosproject/kubevirtnode/codec/KubevirtApiConfig.json
+++ b/apps/kubevirt-node/app/src/test/resources/org/onosproject/kubevirtnode/codec/KubevirtApiConfig.json
@@ -5,5 +5,6 @@
   "token": "token",
   "caCertData": "caCertData",
   "clientCertData": "clientCertData",
-  "clientKeyData": "clientKeyData"
+  "clientKeyData": "clientKeyData",
+  "serviceFqdn": "kubevirt.edgestack.svc.cluster.local"
 }
\ No newline at end of file