Support to specify BootFileName and ServerName in DHCP Option

Change-Id: I0b1cc4af29db933e22b42f999c56a2189a967b97
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index 4410d70..897f473 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -60,6 +60,7 @@
 
     public static final String PCI_VENDOR_INFO = "pci_vendor_info";
     public static final String DIRECT = "direct";
+    public static final String BAREMETAL = "baremetal";
     public static final String PCISLOT = "pci_slot";
 
     public static final String ANNOTATION_NETWORK_ID = "networkId";
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java
index 19551ea..adc6bad 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java
@@ -56,6 +56,7 @@
 import org.openstack4j.model.network.Network;
 import org.openstack4j.model.network.Port;
 import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -80,16 +81,21 @@
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END;
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_LeaseTime;
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
+import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RequestedParameters;
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RouterAddress;
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_SubnetMask;
 import static org.onlab.packet.DHCP.MsgType.DHCPACK;
 import static org.onlab.packet.DHCP.MsgType.DHCPOFFER;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.BAREMETAL;
 import static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_DHCP_RULE;
 import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.DHCP_SERVER_MAC;
 import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.DHCP_SERVER_MAC_DEFAULT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getBroadcastAddr;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getDhcpFullBootFileName;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getDhcpServerName;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getDhcpStaticBootFileName;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -124,6 +130,9 @@
     private static final int DHCP_OPTION_DNS_LENGTH = 8;
     private static final int DHCP_OPTION_MTU_LENGTH = 2;
 
+    private static final byte DHCP_OPTION_SERVER_NAME_CODE = (byte) 66;
+    private static final byte DHCP_OPTION_BOOT_FILE_NAME_CODE = (byte) 67;
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
 
@@ -341,7 +350,7 @@
                     dhcpRequest,
                     packetType,
                     Ip4Address.valueOf(fixedIp.getIpAddress()),
-                    osSubnet);
+                    (NeutronPort) port, osSubnet);
 
             udpReply.setPayload(dhcpReply);
             ipv4Reply.setPayload(udpReply);
@@ -368,7 +377,7 @@
         }
 
         private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
-                                    Subnet osSubnet) {
+                                    NeutronPort port, Subnet osSubnet) {
             Ip4Address gatewayIp = clusterService.getLocalNode().ip().getIp4Address();
             int subnetPrefixLen = IpPrefix.valueOf(osSubnet.getCidr()).prefixLength();
 
@@ -416,6 +425,12 @@
                 options.add(doRouterAddr(osSubnet));
             }
 
+            // sets TFTP and bootfilename for PXE boot
+            if (BAREMETAL.equalsIgnoreCase(port.getvNicType())) {
+                options.add(doTftp(port));
+                options.add(doBootfileName(request, port));
+            }
+
             // end option
             options.add(doEnd());
 
@@ -512,6 +527,39 @@
             return option;
         }
 
+        private DhcpOption doTftp(NeutronPort port) {
+            String serverName = getDhcpServerName(port);
+            log.info("DHCP server name : {}", serverName);
+
+            DhcpOption option = new DhcpOption();
+            option.setCode(DHCP_OPTION_SERVER_NAME_CODE);
+            option.setLength((byte) serverName.length());
+            option.setData(serverName.getBytes());
+
+            return option;
+        }
+
+        private DhcpOption doBootfileName(DHCP request, NeutronPort port) {
+            String bootStaticFileName = getDhcpStaticBootFileName(port);
+            String bootFullFileName = getDhcpFullBootFileName(port);
+
+            DhcpOption option = new DhcpOption();
+            option.setCode(DHCP_OPTION_BOOT_FILE_NAME_CODE);
+
+            DhcpOption requestOption = request.getOption(OptionCode_RequestedParameters);
+            if (requestOption.getLength() > 30) {
+                log.info("DHCP static boot file name {}", bootStaticFileName);
+                option.setLength((byte) bootStaticFileName.length());
+                option.setData(bootStaticFileName.getBytes());
+            } else {
+                log.info("DHCP full boot file path {}", bootFullFileName);
+                option.setLength((byte) bootFullFileName.length());
+                option.setData(bootFullFileName.getBytes());
+            }
+
+            return option;
+        }
+
         private DhcpOption doClasslessSr(Subnet osSubnet) {
             DhcpOption option = new DhcpOption();
             option.setCode(OptionCode_Classless_Static_Route.getValue());
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 61ba3f8..7549b90 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -20,6 +20,7 @@
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.base.Charsets;
 import com.google.common.base.Strings;
@@ -95,6 +96,7 @@
 import org.openstack4j.model.network.SecurityGroup;
 import org.openstack4j.model.network.Subnet;
 import org.openstack4j.openstack.OSFactory;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
 import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -1468,6 +1470,53 @@
     }
 
     /**
+     * Obtains the DHCP server name from option.
+     *
+     * @param port neutron port
+     * @return server name
+     */
+    public static String getDhcpServerName(NeutronPort port) {
+        return getDhcpOptionValue(port, "server-ip-address");
+    }
+
+    /**
+     * Obtains the DHCP static boot file name from option.
+     *
+     * @param port neutron port
+     * @return DHCP static boot file name
+     */
+    public static String getDhcpStaticBootFileName(NeutronPort port) {
+        return getDhcpOptionValue(port, "tag:!ipxe,67");
+    }
+
+    /**
+     * Obtains the DHCP full boot file name from option.
+     *
+     * @param port neutron port
+     * @return DHCP full boot file name
+     */
+    public static String getDhcpFullBootFileName(NeutronPort port) {
+        return getDhcpOptionValue(port, "tag:ipxe,67");
+    }
+
+    private static String getDhcpOptionValue(NeutronPort port, String optionNameStr) {
+        ObjectNode node = modelEntityToJson(port, NeutronPort.class);
+
+        if (node != null) {
+            JsonNode portJson = node.get("port");
+            ArrayNode options = (ArrayNode) portJson.get("extra_dhcp_opts");
+            for (JsonNode option : options) {
+                String optionName = option.get("optName").asText();
+                if (StringUtils.equals(optionName, optionNameStr)) {
+                    return option.get("optValue").asText();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Builds up and a complete endpoint URL from gateway node.
      *
      * @param node gateway node