Use metadata secret var in openstack node, support PUT/POST/DELETE

Change-Id: Id744b6443b49a3a0e73f9d497462f48ba64ab0a7
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackMetadataProxyHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackMetadataProxyHandler.java
index 1a225ea..c7fc417 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackMetadataProxyHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackMetadataProxyHandler.java
@@ -16,13 +16,11 @@
 
 package org.onosproject.openstacknetworking.impl;
 
-import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
 import org.apache.commons.lang.StringUtils;
 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.Modified;
-import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.http.Header;
@@ -31,8 +29,10 @@
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
 import org.apache.http.client.methods.HttpRequestBase;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
@@ -46,8 +46,6 @@
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.TCP;
 import org.onlab.packet.TpPort;
-import org.onlab.util.Tools;
-import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
 import org.onosproject.cluster.NodeId;
@@ -72,13 +70,12 @@
 import org.onosproject.openstacknode.api.OpenstackNodeListener;
 import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.openstack4j.model.network.Port;
-import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.util.Dictionary;
 import java.util.Objects;
+import java.util.Set;
 
 import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
@@ -121,17 +118,13 @@
 
     private static final String HTTP_GET_METHOD = "GET";
     private static final String HTTP_POST_METHOD = "POST";
-
-    private static final String METADATA_SECRET = "metadataSecret";
-    private static final String DEFAULT_METADATA_SECRET = "nova";
+    private static final String HTTP_PUT_METHOD = "PUT";
+    private static final String HTTP_DELETE_METHOD = "DELETE";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ComponentConfigService configService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -152,13 +145,11 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackFlowRuleService osFlowRuleService;
 
-    @Property(name = METADATA_SECRET, value = DEFAULT_METADATA_SECRET,
-            label = "Metadata secret")
-    private String metadataSecret = DEFAULT_METADATA_SECRET;
-
     private final PacketProcessor packetProcessor = new InternalPacketProcessor();
     private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
 
+    private Set<String> excludedHeaders = ImmutableSet.of("content-type", "content-length");
+
     private ApplicationId appId;
     private NodeId localNodeId;
 
@@ -166,7 +157,6 @@
     protected void activate() {
         appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
         localNodeId = clusterService.getLocalNode().id();
-        configService.registerProperties(getClass());
         osNodeService.addListener(osNodeListener);
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
         leadershipService.runForLeadership(appId.name());
@@ -177,32 +167,21 @@
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(packetProcessor);
-        configService.unregisterProperties(getClass(), false);
         osNodeService.removeListener(osNodeListener);
         leadershipService.withdraw(appId.name());
 
         log.info("Stopped");
     }
 
-    @Modified
-    protected void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        String updatedMetadataSecret;
-
-        updatedMetadataSecret = Tools.get(properties, METADATA_SECRET);
-
-        if (!Strings.isNullOrEmpty(updatedMetadataSecret) &&
-                !updatedMetadataSecret.equals(metadataSecret)) {
-            metadataSecret = updatedMetadataSecret;
-        }
-
-        log.info("Modified");
-    }
-
     private class InternalPacketProcessor implements PacketProcessor {
 
         @Override
         public void process(PacketContext context) {
+
+            if (!useMetadataProxy()) {
+                return;
+            }
+
             if (context.isHandled()) {
                 return;
             }
@@ -224,12 +203,16 @@
                 return;
             }
 
+            // (three-way handshaking)
+            // reply TCP SYN-ACK packet with receiving TCP SYN packet
             if (tcpPacket.getFlags() == SYN_FLAG) {
                 Ethernet ethReply = buildTcpSynAckPacket(ethPacket, ipv4Packet, tcpPacket);
                 sendReply(context, ethReply);
                 return;
             }
 
+            // (four-way handshaking)
+            // reply TCP ACK and TCP FIN-ACK packets with receiving TCP FIN-ACK packet
             if (tcpPacket.getFlags() == FIN_ACK_FLAG) {
                 Ethernet ackReply = buildTcpAckPacket(ethPacket, ipv4Packet, tcpPacket);
                 sendReply(context, ackReply);
@@ -238,6 +221,7 @@
                 return;
             }
 
+            // normal TCP data transmission
             Data data = (Data) tcpPacket.getPayload();
             byte[] byteData = data.getData();
 
@@ -252,7 +236,7 @@
                 }
 
                 // attempt to send HTTP request to the meta-data server (nova-api),
-                // obtain the HTTP response
+                // obtain the HTTP response, relay the response to VM through packet-out
                 CloseableHttpResponse proxyResponse = proxyHttpRequest(request, instPort);
 
                 if (proxyResponse == null) {
@@ -434,19 +418,30 @@
                 return null;
             }
 
-            log.info("Sending request to metadata endpoint {}...", url);
-
             HttpRequestBase request;
 
-            switch (oldRequest.getRequestLine().getMethod()) {
+            String method = oldRequest.getRequestLine().getMethod().toUpperCase();
+
+            log.info("Sending HTTP {} request to metadata endpoint {}...", method, url);
+
+            switch (method) {
                 case HTTP_GET_METHOD:
                     request = new HttpGet(url);
                     break;
                 case HTTP_POST_METHOD:
                     request = new HttpPost(url);
-                    HttpEntityEnclosingRequest entityRequest =
+                    HttpEntityEnclosingRequest postRequest =
                             (HttpEntityEnclosingRequest) oldRequest;
-                    ((HttpPost) request).setEntity(entityRequest.getEntity());
+                    ((HttpPost) request).setEntity(postRequest.getEntity());
+                    break;
+                case HTTP_PUT_METHOD:
+                    request = new HttpPut(url);
+                    HttpEntityEnclosingRequest putRequest =
+                            (HttpEntityEnclosingRequest) oldRequest;
+                    ((HttpPut) request).setEntity(putRequest.getEntity());
+                    break;
+                case HTTP_DELETE_METHOD:
+                    request = new HttpDelete(url);
                     break;
                 default:
                     request = new HttpGet(url);
@@ -455,6 +450,13 @@
 
             // configure headers from original HTTP request
             for (Header header : oldRequest.getAllHeaders()) {
+                if (method.equals(HTTP_POST_METHOD) ||
+                        method.equals(HTTP_PUT_METHOD)) {
+                    // we DO NOT add duplicated HTTP headers for POST and PUT methods
+                    if (excludedHeaders.contains(header.getName().toLowerCase())) {
+                        continue;
+                    }
+                }
                 request.addHeader(header);
             }
 
@@ -463,11 +465,12 @@
             Port port = osNetworkService.port(instPort.portId());
 
             request.addHeader(new BasicHeader(INSTANCE_ID_HEADER, port.getDeviceId()));
-            request.addHeader(new BasicHeader(INSTANCE_ID_SIGNATURE_HEADER,
-                    hmacEncrypt(metadataSecret, port.getDeviceId())));
             request.addHeader(new BasicHeader(TENANT_ID_HEADER, port.getTenantId()));
-            request.addHeader(new BasicHeader(
-                    FORWARDED_FOR_HEADER, instPort.ipAddress().toString()));
+            request.addHeader(new BasicHeader(FORWARDED_FOR_HEADER, instPort.ipAddress().toString()));
+            if (metadataSecret() != null) {
+                request.addHeader(new BasicHeader(INSTANCE_ID_SIGNATURE_HEADER,
+                        hmacEncrypt(metadataSecret(), port.getDeviceId())));
+            }
 
             try {
                 return client.execute(request);
@@ -508,7 +511,7 @@
             // do not allow to proceed without leadership
             NodeId leader = leadershipService.getLeader(appId.name());
             return Objects.equals(localNodeId, leader) &&
-                    event.subject().type() == COMPUTE;
+                    event.subject().type() == COMPUTE && useMetadataProxy();
         }
 
         @Override
@@ -559,6 +562,28 @@
         }
     }
 
+    private boolean useMetadataProxy() {
+        OpenstackNode gw = osNodeService.completeNodes(CONTROLLER)
+                .stream().findFirst().orElse(null);
+
+        if (gw != null && gw.neutronConfig() != null) {
+            return gw.neutronConfig().useMetadataProxy();
+        }
+
+        return false;
+    }
+
+    private String metadataSecret() {
+        OpenstackNode controller = osNodeService.completeNodes(CONTROLLER)
+                .stream().findFirst().orElse(null);
+
+        if (controller != null && controller.neutronConfig() != null) {
+            return controller.neutronConfig().metadataProxySecret();
+        }
+
+        return null;
+    }
+
     /**
      * Implements Http packet format.
      */
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
index 0b713b4..571890b 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
@@ -26,6 +26,8 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.openstacknode.api.DefaultKeystoneConfig;
+import org.onosproject.openstacknode.api.DefaultNeutronConfig;
 import org.onosproject.openstacknode.api.DefaultOpenstackAuth;
 import org.onosproject.openstacknode.api.DefaultOpenstackNode;
 import org.onosproject.openstacknode.api.DpdkConfig;
@@ -98,6 +100,8 @@
             .register(DefaultOpenstackAuth.Perspective.class)
             .register(DefaultOpenstackAuth.Protocol.class)
             .register(DefaultOpenstackSshAuth.class)
+            .register(DefaultKeystoneConfig.class)
+            .register(DefaultNeutronConfig.class)
             .register(Collection.class)
             .build();