cloudstack-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "ASF GitHub Bot (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (CLOUDSTACK-9776) extra DHCP option support
Date Tue, 21 Nov 2017 10:45:01 GMT

    [ https://issues.apache.org/jira/browse/CLOUDSTACK-9776?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16260559#comment-16260559 ] 

ASF GitHub Bot commented on CLOUDSTACK-9776:
--------------------------------------------

fmaximus closed pull request #2049: [4.11] CLOUDSTACK-9776: extra DHCP options support with Nuage VSP
URL: https://github.com/apache/cloudstack/pull/2049
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java
index 2cd41494244..a02c8cfdbe2 100644
--- a/api/src/com/cloud/network/Network.java
+++ b/api/src/com/cloud/network/Network.java
@@ -47,7 +47,7 @@
         private static List<Service> supportedServices = new ArrayList<Service>();
 
         public static final Service Vpn = new Service("Vpn", Capability.SupportedVpnProtocols, Capability.VpnTypes);
-        public static final Service Dhcp = new Service("Dhcp");
+        public static final Service Dhcp = new Service("Dhcp", Capability.ExtraDhcpOptions);
         public static final Service Dns = new Service("Dns", Capability.AllowDnsSuffixModification);
         public static final Service Gateway = new Service("Gateway");
         public static final Service Firewall = new Service("Firewall", Capability.SupportedProtocols, Capability.MultipleIps, Capability.TrafficStatistics,
@@ -218,6 +218,7 @@ public static Provider getProvider(String providerName) {
         public static final Capability RegionLevelVpc = new Capability("RegionLevelVpc");
         public static final Capability NoVlan = new Capability("NoVlan");
         public static final Capability PublicAccess = new Capability("PublicAccess");
+        public static final Capability ExtraDhcpOptions = new Capability("ExtraDhcpOptions");
 
         private final String name;
 
diff --git a/api/src/com/cloud/network/element/DhcpServiceProvider.java b/api/src/com/cloud/network/element/DhcpServiceProvider.java
index 2bd6ebcba35..12830f8cec0 100644
--- a/api/src/com/cloud/network/element/DhcpServiceProvider.java
+++ b/api/src/com/cloud/network/element/DhcpServiceProvider.java
@@ -16,6 +16,8 @@
 // under the License.
 package com.cloud.network.element;
 
+import java.util.Map;
+
 import com.cloud.deploy.DeployDestination;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientCapacityException;
@@ -33,4 +35,6 @@ boolean configDhcpSupportForSubnet(Network network, NicProfile nic, VirtualMachi
         throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException;
 
     boolean removeDhcpSupportForSubnet(Network network) throws ResourceUnavailableException;
+
+    boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions);
 }
diff --git a/api/src/com/cloud/vm/NicExtraDhcpOption.java b/api/src/com/cloud/vm/NicExtraDhcpOption.java
new file mode 100644
index 00000000000..f5d9fa0c6b4
--- /dev/null
+++ b/api/src/com/cloud/vm/NicExtraDhcpOption.java
@@ -0,0 +1,41 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.vm;
+
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
+public interface NicExtraDhcpOption extends InternalIdentity, Identity {
+
+    /**
+     * Returns the nic id for which the DHCP option applies
+     * @return nic id
+     */
+    long getNicId();
+
+    /**
+     * Returns the DHCP option code
+     * @return
+     */
+    int getCode();
+
+    /**
+     * Returns the Dhcp value
+     * @return
+     */
+    String getValue();
+}
diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java
index 68425c3b675..178840bfe0b 100644
--- a/api/src/com/cloud/vm/UserVmService.java
+++ b/api/src/com/cloud/vm/UserVmService.java
@@ -194,6 +194,8 @@ UserVm startVirtualMachine(StartVMCmd cmd) throws StorageUnavailableException, E
      * @param memory
      * @param cpuNumber
      * @param customId
+     * @param dhcpOptionMap
+     *           - Maps the dhcp option code and the dhcp value to the network uuid
      * @return UserVm object if successful.
      *
      * @throws InsufficientCapacityException
@@ -208,7 +210,7 @@ UserVm startVirtualMachine(StartVMCmd cmd) throws StorageUnavailableException, E
     UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList,
         Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod,
         String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard,
-        List<Long> affinityGroupIdList, Map<String, String> customParameter, String customId) throws InsufficientCapacityException,
+        List<Long> affinityGroupIdList, Map<String, String> customParameter, String customId, Map<String, Map<Integer, String>> dhcpOptionMap) throws InsufficientCapacityException,
         ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
@@ -267,6 +269,8 @@ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering s
      * @param memory
      * @param cpuNumber
      * @param customId
+     * @param dhcpOptionMap
+     *             - Maps the dhcp option code and the dhcp value to the network uuid
      * @return UserVm object if successful.
      *
      * @throws InsufficientCapacityException
@@ -281,7 +285,7 @@ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering s
     UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
         List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
         HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
-        List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId) throws InsufficientCapacityException,
+        List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap) throws InsufficientCapacityException,
         ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
@@ -338,6 +342,8 @@ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOfferin
      * @param memory
      * @param cpuNumber
      * @param customId
+     * @param dhcpOptionMap
+     *             - Map that maps the DhcpOption code and their value on the Network uuid
      * @return UserVm object if successful.
      *
      * @throws InsufficientCapacityException
@@ -352,7 +358,7 @@ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOfferin
     UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner,
         String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
         String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
-        Map<String, String> customParameters, String customId)
+        Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap)
 
         throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 2d5fe779761..a5bd95f83c5 100644
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -88,6 +88,9 @@
     public static final String UTILIZATION = "utilization";
     public static final String DRIVER = "driver";
     public static final String ROOT_DISK_SIZE = "rootdisksize";
+    public static final String DHCP_OPTIONS_NETWORK_LIST = "dhcpoptionsnetworklist";
+    public static final String DHCP_OPTIONS = "dhcpoptions";
+    public static final String DHCP_PREFIX = "dhcp:";
     public static final String DISPLAY_NAME = "displayname";
     public static final String DISPLAY_NETWORK = "displaynetwork";
     public static final String DISPLAY_NIC = "displaynic";
@@ -111,6 +114,10 @@
     public static final String END_PORT = "endport";
     public static final String ENTRY_TIME = "entrytime";
     public static final String EXPIRES = "expires";
+    public static final String EXTRA_DHCP_OPTION = "extradhcpoption";
+    public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname";
+    public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode";
+    public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue";
     public static final String FENCE = "fence";
     public static final String FETCH_LATEST = "fetchlatest";
     public static final String FIRSTNAME = "firstname";
@@ -242,6 +249,7 @@
     public static final String SCHEDULE = "schedule";
     public static final String SCOPE = "scope";
     public static final String SECRET_KEY = "usersecretkey";
+    public static final String SECONDARY_IP = "secondaryip";
     public static final String SINCE = "since";
     public static final String KEY = "key";
     public static final String SEARCH_BASE = "searchbase";
@@ -309,6 +317,7 @@
     public static final String REMOVE_VLAN = "removevlan";
     public static final String VLAN_ID = "vlanid";
     public static final String ISOLATED_PVLAN = "isolatedpvlan";
+    public static final String ISOLATION_URI = "isolationuri";
     public static final String VM_AVAILABLE = "vmavailable";
     public static final String VM_LIMIT = "vmlimit";
     public static final String VM_TOTAL = "vmtotal";
@@ -410,6 +419,7 @@
     public static final String CAPACITY_IOPS = "capacityiops";
     public static final String NETWORK_SPEED = "networkspeed";
     public static final String BROADCAST_DOMAIN_RANGE = "broadcastdomainrange";
+    public static final String BROADCAST_URI = "broadcasturi";
     public static final String ISOLATION_METHOD = "isolationmethod";
     public static final String ISOLATION_METHODS = "isolationmethods";
     public static final String PHYSICAL_NETWORK_ID = "physicalnetworkid";
@@ -536,6 +546,8 @@
     public static final String NICIRA_NVP_DEVICE_NAME = "niciradevicename";
     public static final String NICIRA_NVP_GATEWAYSERVICE_UUID = "l3gatewayserviceuuid";
     public static final String NICIRA_NVP_L2_GATEWAYSERVICE_UUID = "l2gatewayserviceuuid";
+    public static final String NSX_LOGICAL_SWITCH = "nsxlogicalswitch";
+    public static final String NSX_LOGICAL_SWITCH_PORT = "nsxlogicalswitchport";
     public static final String S3_ACCESS_KEY = "accesskey";
     public static final String S3_SECRET_KEY = "secretkey";
     public static final String S3_END_POINT = "endpoint";
@@ -665,6 +677,7 @@
     public static final String SUPPORTS_PUBLIC_ACCESS = "supportspublicaccess";
     public static final String REGION_LEVEL_VPC = "regionlevelvpc";
     public static final String STRECHED_L2_SUBNET = "strechedl2subnet";
+    public static final String NETWORK_NAME = "networkname";
     public static final String NETWORK_SPANNED_ZONES = "zonesnetworkspans";
     public static final String METADATA = "metadata";
     public static final String PHYSICAL_SIZE = "physicalsize";
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java
index ef03e78bea5..ed2a4b56375 100644
--- a/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java
@@ -17,7 +17,10 @@
 package org.apache.cloudstack.api.command.user.vm;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.apache.log4j.Logger;
 
@@ -39,6 +42,7 @@
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.user.Account;
 import com.cloud.uservm.UserVm;
+import com.cloud.utils.net.Dhcp;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.VirtualMachine;
 
@@ -65,6 +69,10 @@
     @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "Mac Address for the new network")
     private String macaddr;
 
+    @Parameter(name = ApiConstants.DHCP_OPTIONS, type = CommandType.MAP, description = "DHCP options which are passed to the nic"
+            + " Example: dhcpoptions[0].dhcp:114=url&dhcpoptions[0].dhcp:66=www.test.com")
+    private Map dhcpOptions;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -125,6 +133,28 @@ public long getEntityOwnerId() {
         return vm.getAccountId();
     }
 
+    public Map<Integer, String> getDhcpOptionsMap() {
+        Map<Integer, String> dhcpOptionsMap = new HashMap<>();
+        if (dhcpOptions != null && !dhcpOptions.isEmpty()) {
+
+            Collection<Map<String, String>> paramsCollection = this.dhcpOptions.values();
+            for(Map<String, String> dhcpNetworkOptions : paramsCollection) {
+                for (String key : dhcpNetworkOptions.keySet()) {
+                    if (key.startsWith(ApiConstants.DHCP_PREFIX)) {
+                        int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, ""));
+                        dhcpOptionsMap.put(dhcpOptionValue, dhcpNetworkOptions.get(key));
+                    } else {
+                        Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key);
+                        dhcpOptionsMap.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key));
+                    }
+                }
+
+            }
+        }
+
+        return dhcpOptionsMap;
+    }
+
     @Override
     public void execute() {
         CallContext.current().setEventDetails("Vm Id: " + getVmId() + " Network Id: " + getNetworkId());
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index bd2ae6f9c17..548a89d6240 100644
--- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -59,6 +59,7 @@
 import com.cloud.network.Network;
 import com.cloud.network.Network.IpAddresses;
 import com.cloud.uservm.UserVm;
+import com.cloud.utils.net.Dhcp;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.VirtualMachine;
 
@@ -187,6 +188,10 @@
     @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for vm allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin })
     private String deploymentPlanner;
 
+    @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up"
+            + " Example: dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com")
+    private Map dhcpOptionsNetworkList;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -407,6 +412,37 @@ public String getKeyboard() {
         return keyboard;
     }
 
+    public Map<String, Map<Integer, String>> getDhcpOptionsMap() {
+        Map<String, Map<Integer, String>> dhcpOptionsMap = new HashMap<>();
+        if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) {
+
+            Collection<Map<String, String>> paramsCollection = this.dhcpOptionsNetworkList.values();
+            for(Map<String, String> dhcpNetworkOptions : paramsCollection) {
+                String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID);
+
+                if(networkId == null) {
+                    throw new IllegalArgumentException("No networkid specified when providing extra dhcp options.");
+                }
+
+                Map<Integer, String> dhcpOptionsForNetwork = new HashMap<>();
+                dhcpOptionsMap.put(networkId, dhcpOptionsForNetwork);
+
+                for (String key : dhcpNetworkOptions.keySet()) {
+                    if (key.startsWith(ApiConstants.DHCP_PREFIX)) {
+                        int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, ""));
+                        dhcpOptionsForNetwork.put(dhcpOptionValue, dhcpNetworkOptions.get(key));
+                    } else if (!key.equals(ApiConstants.NETWORK_ID)){
+                            Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key);
+                            dhcpOptionsForNetwork.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key));
+                    }
+                }
+
+            }
+        }
+
+        return dhcpOptionsMap;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
index eb03f086c69..ea2d19dfc1c 100644
--- a/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.api.command.user.vm;
 
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -40,6 +41,7 @@
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.user.Account;
 import com.cloud.uservm.UserVm;
+import com.cloud.utils.net.Dhcp;
 import com.cloud.vm.VirtualMachine;
 
 @APICommand(name = "updateVirtualMachine", description="Updates properties of a virtual machine. The VM has to be stopped and restarted for the " +
@@ -121,6 +123,11 @@
             description = "optional boolean field, which indicates if details should be cleaned up or not (if set to true, details removed for this resource, details field ignored; if false or not set, no action)")
     private Boolean cleanupDetails;
 
+    @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up"
+            + " Example: dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com")
+    private Map dhcpOptionsNetworkList;
+
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -182,6 +189,37 @@ public boolean isCleanupDetails(){
         return cleanupDetails == null ? false : cleanupDetails.booleanValue();
     }
 
+    public Map<String, Map<Integer, String>> getDhcpOptionsMap() {
+        Map<String, Map<Integer, String>> dhcpOptionsMap = new HashMap<>();
+        if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) {
+
+            Collection<Map<String, String>> paramsCollection = this.dhcpOptionsNetworkList.values();
+            for(Map<String, String> dhcpNetworkOptions : paramsCollection) {
+                String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID);
+
+                if(networkId == null) {
+                    throw new IllegalArgumentException("No networkid specified when providing extra dhcp options.");
+                }
+
+                Map<Integer, String> dhcpOptionsForNetwork = new HashMap<>();
+                dhcpOptionsMap.put(networkId, dhcpOptionsForNetwork);
+
+                for (String key : dhcpNetworkOptions.keySet()) {
+                    if (key.startsWith(ApiConstants.DHCP_PREFIX)) {
+                        int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, ""));
+                        dhcpOptionsForNetwork.put(dhcpOptionValue, dhcpNetworkOptions.get(key));
+                    } else if (!key.equals(ApiConstants.NETWORK_ID)) {
+                        Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key);
+                        dhcpOptionsForNetwork.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key));
+                    }
+                }
+
+            }
+        }
+
+        return dhcpOptionsMap;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/org/apache/cloudstack/api/response/NicExtraDhcpOptionResponse.java b/api/src/org/apache/cloudstack/api/response/NicExtraDhcpOptionResponse.java
new file mode 100644
index 00000000000..4af89a37d8d
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/response/NicExtraDhcpOptionResponse.java
@@ -0,0 +1,99 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.apache.cloudstack.api.response;
+
+
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+
+import com.cloud.serializer.Param;
+
+@EntityReference(value = NicExtraDhcpOptionResponse.class)
+public class NicExtraDhcpOptionResponse extends BaseResponse {
+
+    @SerializedName(ApiConstants.ID)
+    @Param(description = "the ID of the extra dhcp option")
+    private String id;
+
+    @SerializedName(ApiConstants.NIC_ID)
+    @Param(description = "the ID of the nic")
+    private String nicId;
+
+    @SerializedName(ApiConstants.EXTRA_DHCP_OPTION_NAME)
+    @Param(description = "the name of the extra DHCP option")
+    private String codeName;
+
+    @SerializedName(ApiConstants.EXTRA_DHCP_OPTION_CODE)
+    @Param(description = "the extra DHCP option code")
+    private int code;
+
+    @SerializedName(ApiConstants.EXTRA_DHCP_OPTION_VALUE)
+    @Param(description = "the extra DHCP option value")
+    private String value;
+
+    public NicExtraDhcpOptionResponse() {
+        super();
+    }
+
+    public NicExtraDhcpOptionResponse(String codeName, int code, String value) {
+        this.codeName = codeName;
+        this.code = code;
+        this.value = value;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getNicId() {
+        return nicId;
+    }
+
+    public void setNicId(String nicId) {
+        this.nicId = nicId;
+    }
+
+    public String getCodeName() {
+        return codeName;
+    }
+
+    public void setCodeName(String codeName) {
+        this.codeName = codeName;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}
diff --git a/api/src/org/apache/cloudstack/api/response/NicResponse.java b/api/src/org/apache/cloudstack/api/response/NicResponse.java
index 7689123cbaf..5c3fd7a75a6 100644
--- a/api/src/org/apache/cloudstack/api/response/NicResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/NicResponse.java
@@ -29,15 +29,15 @@
 @EntityReference(value = Nic.class)
 public class NicResponse extends BaseResponse {
 
-    @SerializedName("id")
+    @SerializedName(ApiConstants.ID)
     @Param(description = "the ID of the nic")
     private String id;
 
-    @SerializedName("networkid")
+    @SerializedName(ApiConstants.NETWORK_ID)
     @Param(description = "the ID of the corresponding network")
     private String networkId;
 
-    @SerializedName("networkname")
+    @SerializedName(ApiConstants.NETWORK_NAME)
     @Param(description = "the name of the corresponding network")
     private String networkName;
 
@@ -53,11 +53,11 @@
     @Param(description = "the ip address of the nic")
     private String ipaddress;
 
-    @SerializedName("isolationuri")
+    @SerializedName(ApiConstants.ISOLATION_URI)
     @Param(description = "the isolation uri of the nic")
     private String isolationUri;
 
-    @SerializedName("broadcasturi")
+    @SerializedName(ApiConstants.BROADCAST_URI)
     @Param(description = "the broadcast uri of the nic")
     private String broadcastUri;
 
@@ -73,7 +73,7 @@
     @Param(description = "true if nic is default, false otherwise")
     private Boolean isDefault;
 
-    @SerializedName("macaddress")
+    @SerializedName(ApiConstants.MAC_ADDRESS)
     @Param(description = "true if nic is default, false otherwise")
     private String macAddress;
 
@@ -89,10 +89,14 @@
     @Param(description = "the IPv6 address of network")
     private String ip6Address;
 
-    @SerializedName("secondaryip")
+    @SerializedName(ApiConstants.SECONDARY_IP)
     @Param(description = "the Secondary ipv4 addr of nic")
     private List<NicSecondaryIpResponse> secondaryIps;
 
+    @SerializedName(ApiConstants.EXTRA_DHCP_OPTION)
+    @Param(description = "the extra dhcp options on the nic", since = "4.11.0")
+    private List<NicExtraDhcpOptionResponse> extraDhcpOptions;
+
     @SerializedName(ApiConstants.DEVICE_ID)
     @Param(description = "device id for the network when plugged into the virtual machine", since = "4.4")
     private String deviceId;
@@ -101,11 +105,11 @@
     @Param(description = "Id of the vm to which the nic belongs")
     private String vmId;
 
-    @SerializedName("nsxlogicalswitch")
+    @SerializedName(ApiConstants.NSX_LOGICAL_SWITCH)
     @Param(description = "Id of the NSX Logical Switch (if NSX based), null otherwise", since="4.6.0")
     private String nsxLogicalSwitch;
 
-    @SerializedName("nsxlogicalswitchport")
+    @SerializedName(ApiConstants.NSX_LOGICAL_SWITCH_PORT)
     @Param(description = "Id of the NSX Logical Switch Port (if NSX based), null otherwise", since="4.6.0")
     private String nsxLogicalSwitchPort;
 
@@ -181,6 +185,10 @@ public void setDeviceId(String deviceId) {
         this.deviceId = deviceId;
     }
 
+    public void setExtraDhcpOptions(List<NicExtraDhcpOptionResponse> extraDhcpOptions) {
+        this.extraDhcpOptions = extraDhcpOptions;
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
diff --git a/engine/api/src/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/com/cloud/vm/VirtualMachineManager.java
index 4b61caaf921..14fead7a057 100644
--- a/engine/api/src/com/cloud/vm/VirtualMachineManager.java
+++ b/engine/api/src/com/cloud/vm/VirtualMachineManager.java
@@ -78,7 +78,7 @@
      */
     void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering, DiskOfferingInfo rootDiskOfferingInfo,
         List<DiskOfferingInfo> dataDiskOfferings, LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, DeploymentPlan plan,
-        HypervisorType hyperType) throws InsufficientCapacityException;
+        HypervisorType hyperType, Map<String, Map<Integer, String>> extraDhcpOptions) throws InsufficientCapacityException;
 
     void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering,
         LinkedHashMap<? extends Network, List<? extends NicProfile>> networkProfiles, DeploymentPlan plan, HypervisorType hyperType) throws InsufficientCapacityException;
diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
index 1d1bc4e4c49..e2a471fef6a 100644
--- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
@@ -93,9 +93,24 @@
         boolean errorIfAlreadySetup, Long domainId, ACLType aclType, Boolean subdomainAccess, Long vpcId, Boolean isDisplayNetworkEnabled)
         throws ConcurrentOperationException;
 
-    void allocate(VirtualMachineProfile vm, LinkedHashMap<? extends Network, List<? extends NicProfile>> networks) throws InsufficientCapacityException,
+    void allocate(VirtualMachineProfile vm, LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, Map<String, Map<Integer, String>> extraDhcpOptions) throws InsufficientCapacityException,
         ConcurrentOperationException;
 
+    /**
+     * configures the provided dhcp options on the given nic.
+     * @param network of the nic
+     * @param nicId
+     * @param extraDhcpOptions
+     */
+    void configureExtraDhcpOptions(Network network, long nicId, Map<Integer, String> extraDhcpOptions);
+
+    /**
+     * configures dhcp options on the given nic.
+     * @param network of the nic
+     * @param nicId
+     */
+    void configureExtraDhcpOptions(Network network, long nicId);
+
     void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws InsufficientCapacityException, ConcurrentOperationException,
         ResourceUnavailableException;
 
@@ -112,6 +127,8 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC
     Pair<? extends NetworkGuru, ? extends Network> implementNetwork(long networkId, DeployDestination dest, ReservationContext context)
         throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
 
+    Map<Integer, String> getExtraDhcpOptions(long nicId);
+
     /**
      * prepares vm nic change for migration
      *
@@ -158,6 +175,8 @@ boolean startNetwork(long networkId, DeployDestination dest, ReservationContext
 
     boolean reallocate(VirtualMachineProfile vm, DataCenterDeployment dest) throws InsufficientCapacityException, ConcurrentOperationException;
 
+    void saveExtraDhcpOptions(String networkUuid, Long nicId, Map<String, Map<Integer, String>> extraDhcpOptionMap);
+
     /**
      * @param requested
      * @param network
diff --git a/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java
index 93f969f7a1c..871745ec601 100644
--- a/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java
@@ -65,14 +65,14 @@ VirtualMachineEntity createVirtualMachine(@QueryParam("id") String id, @QueryPar
         @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize,
         @QueryParam("compute-tags") List<String> computeTags, @QueryParam("root-disk-tags") List<String> rootDiskTags,
         @QueryParam("network-nic-map") Map<String, NicProfile> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan,
-        @QueryParam("root-disk-size") Long rootDiskSize) throws InsufficientCapacityException;
+        @QueryParam("root-disk-size") Long rootDiskSize, @QueryParam("extra-dhcp-option-map") Map<String, Map<Integer, String>> extraDhcpOptionMap) throws InsufficientCapacityException;
 
     @POST
     VirtualMachineEntity createVirtualMachineFromScratch(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("iso-id") String isoId,
         @QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor,
         @QueryParam("os") String os, @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize,
         @QueryParam("compute-tags") List<String> computeTags, @QueryParam("root-disk-tags") List<String> rootDiskTags,
-        @QueryParam("network-nic-map") Map<String, NicProfile> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan) throws InsufficientCapacityException;
+        @QueryParam("network-nic-map") Map<String, NicProfile> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan, @QueryParam("extra-dhcp-option-map") Map<String,  Map<Integer, String>> extraDhcpOptionMap) throws InsufficientCapacityException;
 
     @POST
     NetworkEntity createNetwork(String id, String name, String domainName, String cidr, String gateway);
diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
index ef333563621..fe7fcdfd093 100755
--- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -387,7 +387,7 @@ public void registerGuru(final VirtualMachine.Type type, final VirtualMachineGur
     @DB
     public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering,
             final DiskOfferingInfo rootDiskOfferingInfo, final List<DiskOfferingInfo> dataDiskOfferings,
-            final LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, final DeploymentPlan plan, final HypervisorType hyperType)
+            final LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, final DeploymentPlan plan, final HypervisorType hyperType, final Map<String, Map<Integer, String>> extraDhcpOptions)
                     throws InsufficientCapacityException {
 
         final VMInstanceVO vm = _vmDao.findVMByInstanceName(vmInstanceName);
@@ -415,7 +415,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) throws
 
                 try {
                     if (!vmProfile.getBootArgs().contains("ExternalLoadBalancerVm"))
-                        _networkMgr.allocate(vmProfile, auxiliaryNetworks);
+                        _networkMgr.allocate(vmProfile, auxiliaryNetworks, extraDhcpOptions);
                 } catch (final ConcurrentOperationException e) {
                     throw new CloudRuntimeException("Concurrent operation while trying to allocate resources for the VM", e);
                 }
@@ -451,7 +451,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) throws
     @Override
     public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering,
             final LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, final DeploymentPlan plan, final HypervisorType hyperType) throws InsufficientCapacityException {
-        allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(serviceOffering), new ArrayList<DiskOfferingInfo>(), networks, plan, hyperType);
+        allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(serviceOffering), new ArrayList<DiskOfferingInfo>(), networks, plan, hyperType, null);
     }
 
     private VirtualMachineGuru getVmGuru(final VirtualMachine vm) {
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java
index 2b4995466b5..e588431b6b6 100644
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java
@@ -155,7 +155,7 @@ public void destroyVolume(String volumeEntity) {
     @Override
     public VirtualMachineEntity createVirtualMachine(String id, String owner, String templateId, String hostName, String displayName, String hypervisor, int cpu,
         int speed, long memory, Long diskSize, List<String> computeTags, List<String> rootDiskTags, Map<String, NicProfile> networkNicMap, DeploymentPlan plan,
-        Long rootDiskSize) throws InsufficientCapacityException {
+        Long rootDiskSize, Map<String, Map<Integer, String>> extraDhcpOptionMap) throws InsufficientCapacityException {
 
         // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks,
         // vmEntityManager);
@@ -234,14 +234,14 @@ public VirtualMachineEntity createVirtualMachine(String id, String owner, String
         }
 
         _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(templateId)), computeOffering, rootDiskOfferingInfo, dataDiskOfferings, networkIpMap, plan,
-            hypervisorType);
+            hypervisorType, extraDhcpOptionMap);
 
         return vmEntity;
     }
 
     @Override
     public VirtualMachineEntity createVirtualMachineFromScratch(String id, String owner, String isoId, String hostName, String displayName, String hypervisor, String os,
-        int cpu, int speed, long memory, Long diskSize, List<String> computeTags, List<String> rootDiskTags, Map<String, NicProfile> networkNicMap, DeploymentPlan plan)
+        int cpu, int speed, long memory, Long diskSize, List<String> computeTags, List<String> rootDiskTags, Map<String, NicProfile> networkNicMap, DeploymentPlan plan, Map<String, Map<Integer, String>> extraDhcpOptionMap)
         throws InsufficientCapacityException {
 
         // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, vmEntityManager);
@@ -299,7 +299,7 @@ public VirtualMachineEntity createVirtualMachineFromScratch(String id, String ow
 
         HypervisorType hypervisorType = HypervisorType.valueOf(hypervisor);
 
-        _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(isoId)), computeOffering, rootDiskOfferingInfo, new ArrayList<DiskOfferingInfo>(), networkIpMap, plan, hypervisorType);
+        _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(isoId)), computeOffering, rootDiskOfferingInfo, new ArrayList<DiskOfferingInfo>(), networkIpMap, plan, hypervisorType, extraDhcpOptionMap);
 
         return vmEntity;
     }
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index db0ff980948..1ddff84f991 100644
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -25,6 +25,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -32,10 +33,12 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.vm.NicExtraDhcpOptionVO;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.cloud.entity.api.db.VMNetworkMapVO;
@@ -198,6 +201,7 @@
 import com.cloud.utils.fsm.StateMachine2;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.DomainRouterVO;
+import com.cloud.utils.net.Dhcp;
 import com.cloud.vm.Nic;
 import com.cloud.vm.Nic.ReservationStrategy;
 import com.cloud.vm.NicIpAlias;
@@ -212,6 +216,7 @@
 import com.cloud.vm.VirtualMachineProfile;
 import com.cloud.vm.dao.DomainRouterDao;
 import com.cloud.vm.dao.NicDao;
+import com.cloud.vm.dao.NicExtraDhcpOptionDao;
 import com.cloud.vm.dao.NicIpAliasDao;
 import com.cloud.vm.dao.NicIpAliasVO;
 import com.cloud.vm.dao.NicSecondaryIpDao;
@@ -269,6 +274,8 @@
     @Inject
     protected NicIpAliasDao _nicIpAliasDao;
     @Inject
+    protected NicExtraDhcpOptionDao _nicExtraDhcpOptionDao;
+    @Inject
     protected IPAddressDao _publicIpAddressDao;
     @Inject
     protected IpAddressManager _ipAddrMgr;
@@ -728,7 +735,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) {
 
     @Override
     @DB
-    public void allocate(final VirtualMachineProfile vm, final LinkedHashMap<? extends Network, List<? extends NicProfile>> networks) throws InsufficientCapacityException,
+    public void allocate(final VirtualMachineProfile vm, final LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, final Map<String, Map<Integer, String>> extraDhcpOptions) throws InsufficientCapacityException,
     ConcurrentOperationException {
 
         Transaction.execute(new TransactionCallbackWithExceptionNoReturn<InsufficientCapacityException>() {
@@ -800,6 +807,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) throws
 
                         nics.add(vmNic);
                         vm.addNic(vmNic);
+                        saveExtraDhcpOptions(config.getUuid(), vmNic.getId(), extraDhcpOptions);
                     }
                 }
                 if (nics.size() != size) {
@@ -814,6 +822,24 @@ public void doInTransactionWithoutResult(final TransactionStatus status) throws
         });
     }
 
+    @Override
+    public void saveExtraDhcpOptions(final String networkUuid, final Long nicId, final Map<String, Map<Integer, String>> extraDhcpOptionMap) {
+
+        if(extraDhcpOptionMap != null) {
+            Map<Integer, String> extraDhcpOption = extraDhcpOptionMap.get(networkUuid);
+            if(extraDhcpOption != null) {
+                List<NicExtraDhcpOptionVO> nicExtraDhcpOptionList = new LinkedList<>();
+
+                for (Integer code : extraDhcpOption.keySet()) {
+                    Dhcp.DhcpOptionCode.valueOfInt(code); //check if code is supported or not.
+                    NicExtraDhcpOptionVO nicExtraDhcpOptionVO = new NicExtraDhcpOptionVO(nicId, code, extraDhcpOption.get(code));
+                    nicExtraDhcpOptionList.add(nicExtraDhcpOptionVO);
+                }
+                _nicExtraDhcpOptionDao.saveExtraDhcpOptions(nicExtraDhcpOptionList);
+            }
+        }
+    }
+
     @DB
     @Override
     public Pair<NicProfile, Integer> allocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm)
@@ -1141,6 +1167,15 @@ public void implementNetworkElementsAndResources(final DeployDestination dest, f
             }
         }
 
+
+        //Reset the extra DHCP option that may have been cleared per nic.
+        List<NicVO> nicVOs = _nicDao.listByNetworkId(network.getId());
+        for(NicVO nicVO : nicVOs) {
+            if(nicVO.getState() == Nic.State.Reserved) {
+                configureExtraDhcpOptions(network, nicVO.getId());
+            }
+        }
+
         for (final NetworkElement element : networkElements) {
             if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) {
                 ((AggregatedCommandExecutor)element).prepareAggregatedExecution(network, dest);
@@ -1457,6 +1492,22 @@ public int getResourceCount(Network network){
         return resourceCount;
         }
 
+    @Override
+    public void configureExtraDhcpOptions(Network network, long nicId, Map<Integer, String> extraDhcpOptions) {
+        if(_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)) {
+            if (_networkModel.getNetworkServiceCapabilities(network.getId(), Service.Dhcp).containsKey(Capability.ExtraDhcpOptions)) {
+                DhcpServiceProvider sp = getDhcpServiceProvider(network);
+                sp.setExtraDhcpOptions(network, nicId, extraDhcpOptions);
+            }
+        }
+    }
+
+    @Override
+    public void configureExtraDhcpOptions(Network network, long nicId) {
+        Map<Integer, String> extraDhcpOptions = getExtraDhcpOptions(nicId);
+        configureExtraDhcpOptions(network, nicId, extraDhcpOptions);
+    }
+
     @Override
     public void finalizeUpdateInSequence(Network network, boolean success) {
         List<Provider> providers = getNetworkProviders(network.getId());
@@ -1593,9 +1644,20 @@ public NicProfile prepareNic(final VirtualMachineProfile vmProfile, final Deploy
 
         profile.setSecurityGroupEnabled(_networkModel.isSecurityGroupSupportedInNetwork(network));
         guru.updateNicProfile(profile, network);
+
+        configureExtraDhcpOptions(network, nicId);
         return profile;
     }
 
+    @Override
+    public Map<Integer, String> getExtraDhcpOptions(long nicId) {
+        List<NicExtraDhcpOptionVO> nicExtraDhcpOptionVOList = _nicExtraDhcpOptionDao.listByNicId(nicId);
+        return nicExtraDhcpOptionVOList
+                .stream()
+                .collect(Collectors.toMap(NicExtraDhcpOptionVO::getCode, NicExtraDhcpOptionVO::getValue));
+    }
+
+
     @Override
     public void prepareNicForMigration(final VirtualMachineProfile vm, final DeployDestination dest) {
         if(vm.getType().equals(VirtualMachine.Type.DomainRouter) && (vm.getHypervisorType().equals(HypervisorType.KVM) || vm.getHypervisorType().equals(HypervisorType.VMware))) {
@@ -2949,7 +3011,7 @@ public boolean reallocate(final VirtualMachineProfile vm, final DataCenterDeploy
                 @Override
                 public void doInTransactionWithoutResult(final TransactionStatus status) throws InsufficientCapacityException {
                     cleanupNics(vm);
-                    allocate(vm, profiles);
+                    allocate(vm, profiles, null);
                 }
             });
         }
diff --git a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index 2075b933b64..8a0d7cdde5c 100644
--- a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -215,6 +215,7 @@
   <bean id="networkServiceMapDaoImpl" class="com.cloud.network.dao.NetworkServiceMapDaoImpl" />
   <bean id="nicDaoImpl" class="com.cloud.vm.dao.NicDaoImpl" />
   <bean id="nicDetailsDaoImpl" class="com.cloud.vm.dao.NicDetailsDaoImpl" />
+  <bean id="nicExtraDhcpOptionDaoImpl" class="com.cloud.vm.dao.NicExtraDhcpOptionDaoImpl" />
   <bean id="nicSecondaryIpDaoImpl" class="com.cloud.vm.dao.NicSecondaryIpDaoImpl" />
   <bean id="nicIpAliasDaoImpl" class="com.cloud.vm.dao.NicIpAliasDaoImpl" />
   <bean id="objectInDataStoreDaoImpl" class="org.apache.cloudstack.storage.db.ObjectInDataStoreDaoImpl" />
diff --git a/engine/schema/src/com/cloud/vm/NicExtraDhcpOptionVO.java b/engine/schema/src/com/cloud/vm/NicExtraDhcpOptionVO.java
new file mode 100644
index 00000000000..7b6d28fb5ff
--- /dev/null
+++ b/engine/schema/src/com/cloud/vm/NicExtraDhcpOptionVO.java
@@ -0,0 +1,82 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.vm;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.UUID;
+
+@Entity
+@Table(name = "nic_extra_dhcp_options")
+public class NicExtraDhcpOptionVO implements NicExtraDhcpOption {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+
+    @Column(name = "uuid")
+    String uuid = UUID.randomUUID().toString();
+
+    @Column(name = "nic_id")
+    private long nicId;
+
+    @Column(name="code")
+    private int code;
+
+    @Column(name = "value")
+    private String value;
+
+    public NicExtraDhcpOptionVO (){
+
+    }
+
+    public NicExtraDhcpOptionVO (long nicId, int code, String value) {
+        this.nicId = nicId;
+        this.code = code;
+        this.value = value;
+    }
+
+    @Override
+    public long getNicId() {
+        return nicId;
+    }
+
+    @Override
+    public int getCode() {
+        return code;
+    }
+
+    @Override
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public String getUuid() {
+        return uuid;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+}
diff --git a/engine/schema/src/com/cloud/vm/dao/NicExtraDhcpOptionDao.java b/engine/schema/src/com/cloud/vm/dao/NicExtraDhcpOptionDao.java
new file mode 100644
index 00000000000..69d9c00e1e0
--- /dev/null
+++ b/engine/schema/src/com/cloud/vm/dao/NicExtraDhcpOptionDao.java
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.vm.dao;
+
+import java.util.List;
+
+import com.cloud.utils.db.GenericDao;
+import com.cloud.vm.NicExtraDhcpOptionVO;
+
+public interface NicExtraDhcpOptionDao extends GenericDao<NicExtraDhcpOptionVO, Long> {
+    List<NicExtraDhcpOptionVO> listByNicId(long nicId);
+
+    /**
+     * Persists list of NicExtraDhcpOptionVO
+     * @param extraDhcpOptions
+     */
+    void saveExtraDhcpOptions(List<NicExtraDhcpOptionVO> extraDhcpOptions);
+}
diff --git a/engine/schema/src/com/cloud/vm/dao/NicExtraDhcpOptionDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/NicExtraDhcpOptionDaoImpl.java
new file mode 100644
index 00000000000..3056c73938e
--- /dev/null
+++ b/engine/schema/src/com/cloud/vm/dao/NicExtraDhcpOptionDaoImpl.java
@@ -0,0 +1,77 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.vm.dao;
+
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+import com.cloud.utils.db.DB;
+import com.cloud.utils.db.GenericDaoBase;
+
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.vm.NicExtraDhcpOption;
+import com.cloud.vm.NicExtraDhcpOptionVO;
+
+@Component
+public class NicExtraDhcpOptionDaoImpl  extends GenericDaoBase<NicExtraDhcpOptionVO, Long> implements NicExtraDhcpOptionDao {
+    private SearchBuilder<NicExtraDhcpOptionVO> AllFieldsSearch;
+
+    protected NicExtraDhcpOptionDaoImpl() {
+        super();
+
+        AllFieldsSearch = createSearchBuilder();
+        AllFieldsSearch.and("nic_id", AllFieldsSearch.entity().getNicId(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.and("code", AllFieldsSearch.entity().getCode(), SearchCriteria.Op.IN);
+
+        AllFieldsSearch.done();
+    }
+
+    @DB()
+    @Override
+    public List<NicExtraDhcpOptionVO> listByNicId(long nicId) {
+        SearchCriteria<NicExtraDhcpOptionVO> sc = AllFieldsSearch.create();
+        sc.setParameters("nic_id", nicId);
+
+        return listBy(sc);
+    }
+
+    @DB()
+    @Override
+    public void saveExtraDhcpOptions(List<NicExtraDhcpOptionVO> extraDhcpOptions) {
+        if (extraDhcpOptions.isEmpty()) {
+            return;
+        }
+
+        extraDhcpOptions
+                .stream()
+                .map(NicExtraDhcpOption::getNicId)
+                .distinct()
+                .forEach(this::removeByNicId);
+
+        extraDhcpOptions.stream()
+                .forEach(this::persist);
+    }
+
+    public void removeByNicId(long nicId) {
+        SearchCriteria<NicExtraDhcpOptionVO> sc = AllFieldsSearch.create();
+        sc.setParameters("nic_id", nicId);
+        expunge(sc);
+    }
+
+}
diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
index 77f77dd93d6..5696048b847 100644
--- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
+++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
@@ -180,4 +180,9 @@ public boolean removeDhcpSupportForSubnet(Network network) {
         return true;
     }
 
+    @Override
+    public boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions) {
+        return false;
+    }
+
 }
diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
index 4440b2e2009..62ca29c7e44 100644
--- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
+++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
@@ -374,4 +374,9 @@ public boolean removeDhcpSupportForSubnet(Network network)
                throws ResourceUnavailableException {
        return false;
     }
+
+    @Override
+    public boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions) {
+        return false;
+    }
 }
diff --git a/plugins/network-elements/nuage-vsp/src/com/cloud/agent/api/element/ExtraDhcpOptionsVspCommand.java b/plugins/network-elements/nuage-vsp/src/com/cloud/agent/api/element/ExtraDhcpOptionsVspCommand.java
new file mode 100644
index 00000000000..45f260182d7
--- /dev/null
+++ b/plugins/network-elements/nuage-vsp/src/com/cloud/agent/api/element/ExtraDhcpOptionsVspCommand.java
@@ -0,0 +1,86 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.agent.api.element;
+
+import net.nuage.vsp.acs.client.api.model.VspNetwork;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import java.util.Map;
+import java.util.Objects;
+
+import com.cloud.agent.api.Command;
+
+public class ExtraDhcpOptionsVspCommand extends Command {
+    private final VspNetwork network;
+    private final String nicUuid;
+    private final Map<Integer, String> dhcpOptions;
+
+    public ExtraDhcpOptionsVspCommand (VspNetwork network, String nicUuid, Map<Integer, String> dhcpOptions) {
+        super();
+        this.network = network;
+        this.nicUuid = nicUuid;
+        this.dhcpOptions = dhcpOptions;
+    }
+
+    public VspNetwork getNetwork() {
+        return network;
+    }
+
+    public String getNicUuid() {
+        return nicUuid;
+    }
+
+    public Map<Integer, String> getDhcpOptions() {
+        return dhcpOptions;
+    }
+
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof ExtraDhcpOptionsVspCommand)) {
+            return false;
+        }
+
+        ExtraDhcpOptionsVspCommand that = (ExtraDhcpOptionsVspCommand) o;
+
+        return super.equals(that)
+                && Objects.equals(network, that.network)
+                && Objects.equals(nicUuid, that.nicUuid)
+                && Objects.equals(dhcpOptions, that.dhcpOptions);
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder()
+                .appendSuper(super.hashCode())
+                .append(nicUuid)
+                .append(network)
+                .append(dhcpOptions)
+                .toHashCode();
+    }
+}
diff --git a/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java b/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java
index d7b422b9853..16cbc9dbfa7 100644
--- a/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java
+++ b/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java
@@ -59,6 +59,7 @@
 import com.cloud.agent.api.StartupVspCommand;
 import com.cloud.agent.api.element.ApplyAclRuleVspCommand;
 import com.cloud.agent.api.element.ApplyStaticNatVspCommand;
+import com.cloud.agent.api.element.ExtraDhcpOptionsVspCommand;
 import com.cloud.agent.api.element.ImplementVspCommand;
 import com.cloud.agent.api.element.ShutDownVpcVspCommand;
 import com.cloud.agent.api.element.ShutDownVspCommand;
@@ -252,7 +253,8 @@ public boolean applyIps(Network network, List<? extends PublicIpAddress> ipAddre
                     Capability.MultipleIps, "true"
             ))
             .put(Service.Dhcp, ImmutableMap.of(
-                    Capability.DhcpAccrossMultipleSubnets, "true"
+                    Capability.DhcpAccrossMultipleSubnets, "true",
+                    Capability.ExtraDhcpOptions, "true"
             ))
             .put(Service.NetworkACL, ImmutableMap.of(
                     Capability.SupportedProtocols, "tcp,udp,icmp"
@@ -515,6 +517,23 @@ public boolean removeDhcpSupportForSubnet(Network network) throws ResourceUnavai
         return true;
     }
 
+    @Override
+    public boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions) {
+        VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network);
+        HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(network.getPhysicalNetworkId());
+        NicVO nic = _nicDao.findById(nicId);
+
+        ExtraDhcpOptionsVspCommand extraDhcpOptionsVspCommand = new ExtraDhcpOptionsVspCommand(vspNetwork, nic.getUuid(), dhcpOptions);
+        Answer answer = _agentMgr.easySend(nuageVspHost.getId(), extraDhcpOptionsVspCommand);
+
+        if (answer == null || !answer.getResult()) {
+            s_logger.error("[setExtraDhcpOptions] setting extra DHCP options for nic " + nic.getUuid() + " failed.");
+            return false;
+        }
+
+        return true;
+    }
+
 
     @Override
     public boolean applyStaticNats(Network config, List<? extends StaticNat> rules) throws ResourceUnavailableException {
diff --git a/plugins/network-elements/nuage-vsp/src/com/cloud/network/guru/NuageVspGuestNetworkGuru.java b/plugins/network-elements/nuage-vsp/src/com/cloud/network/guru/NuageVspGuestNetworkGuru.java
index 112d444c710..b1577de0b39 100644
--- a/plugins/network-elements/nuage-vsp/src/com/cloud/network/guru/NuageVspGuestNetworkGuru.java
+++ b/plugins/network-elements/nuage-vsp/src/com/cloud/network/guru/NuageVspGuestNetworkGuru.java
@@ -19,6 +19,7 @@
 
 package com.cloud.network.guru;
 
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -42,6 +43,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
+import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.resourcedetail.VpcDetailVO;
 import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao;
 
@@ -131,6 +133,8 @@
     NetworkDetailsDao _networkDetailsDao;
     @Inject
     VpcDetailsDao _vpcDetailsDao;
+    @Inject
+    NetworkOrchestrationService _networkOrchestrationService;
 
     public NuageVspGuestNetworkGuru() {
         super();
@@ -225,7 +229,9 @@ public Network implement(Network network, NetworkOffering offering, DeployDestin
             implemented.setBroadcastUri(Networks.BroadcastDomainType.Vsp.toUri(broadcastUriStr));
             implemented.setBroadcastDomainType(Networks.BroadcastDomainType.Vsp);
 
-            if (!implement(network.getVpcId(), physicalNetworkId, vspNetwork, _nuageVspEntityBuilder.buildNetworkDhcpOption(network, offering))) {
+            boolean implementSucceeded = implement(network.getVpcId(), physicalNetworkId, vspNetwork, _nuageVspEntityBuilder.buildNetworkDhcpOption(network, offering));
+
+            if (!implementSucceeded) {
                 return null;
             }
 
@@ -383,6 +389,8 @@ public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, D
             // Determine if dhcp options of the other nics in the network need to be updated
             if (vm.getType() == VirtualMachine.Type.DomainRouter && network.getState() != State.Implementing) {
                 updateDhcpOptionsForExistingVms(network, nuageVspHost, vspNetwork, networkHasDns, networkHasDnsCache);
+                //update the extra DHCP options
+
             }
 
             nic.setBroadcastUri(network.getBroadcastUri());
@@ -427,6 +435,10 @@ public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, D
         }
     }
 
+    private void updateExtraDhcpOptionsForExistingVm(Network network, Nic nic) {
+        _networkOrchestrationService.configureExtraDhcpOptions(network, nic.getId());
+    }
+
     private void updateDhcpOptionsForExistingVms(Network network, HostVO nuageVspHost, VspNetwork vspNetwork, boolean networkHasDns, Map<Long, Boolean> networkHasDnsCache)
             throws InsufficientVirtualNetworkCapacityException {
         // Update dhcp options if a VR is added when we are not initiating the network
@@ -437,12 +449,14 @@ private void updateDhcpOptionsForExistingVms(Network network, HostVO nuageVspHos
         List<NicVO> userNics = _nicDao.listByNetworkId(network.getId());
         LinkedListMultimap<Long, VspDhcpVMOption> dhcpOptionsPerDomain = LinkedListMultimap.create();
 
-        for (NicVO userNic : userNics) {
-            if (userNic.getVmType() == VirtualMachine.Type.DomainRouter) {
+        for (Iterator<NicVO> iterator = userNics.iterator(); iterator.hasNext(); ) {
+            NicVO userNic = iterator.next();
+            if (userNic.getVmType() == VirtualMachine.Type.DomainRouter || userNic.getState() != Nic.State.Reserved) {
+                iterator.remove();
                 continue;
             }
 
-            VMInstanceVO userVm  = _vmInstanceDao.findById(userNic.getInstanceId());
+            VMInstanceVO userVm = _vmInstanceDao.findById(userNic.getInstanceId());
             boolean defaultHasDns = getDefaultHasDns(networkHasDnsCache, userNic);
             VspDhcpVMOption dhcpOption = _nuageVspEntityBuilder.buildVmDhcpOption(userNic, defaultHasDns, networkHasDns);
             dhcpOptionsPerDomain.put(userVm.getDomainId(), dhcpOption);
@@ -463,6 +477,10 @@ private void updateDhcpOptionsForExistingVms(Network network, HostVO nuageVspHos
                 throw new InsufficientVirtualNetworkCapacityException("Failed to reserve VM in Nuage VSP.", Network.class, network.getId());
             }
         }
+
+        for (NicVO userNic : userNics) {
+            updateExtraDhcpOptionsForExistingVm(network, userNic);
+        }
     }
 
     private void checkMultipleSubnetsCombinedWithUseData(Network network) {
diff --git a/plugins/network-elements/nuage-vsp/src/com/cloud/network/manager/NuageVspManagerImpl.java b/plugins/network-elements/nuage-vsp/src/com/cloud/network/manager/NuageVspManagerImpl.java
index f57b788a182..1bbef4ec2c7 100644
--- a/plugins/network-elements/nuage-vsp/src/com/cloud/network/manager/NuageVspManagerImpl.java
+++ b/plugins/network-elements/nuage-vsp/src/com/cloud/network/manager/NuageVspManagerImpl.java
@@ -301,10 +301,10 @@ public NuageVspDeviceVO addNuageVspDevice(AddNuageVspDeviceCmd cmd) {
 
 
             if (StringUtils.isNotBlank(cmd.getApiVersion())){
-                if (!clientLoader.getNuageVspManagerClient().isSupportedApiVersion(cmd.getApiVersion())){
+                apiVersion = cmd.getApiVersion();
+                if (!clientLoader.getNuageVspManagerClient().isSupportedApiVersion(apiVersion)){
                     throw new CloudRuntimeException("Unsupported API version : " + cmd.getApiVersion());
                 }
-                apiVersion = cmd.getApiVersion();
             } else {
                 List<NuageVspApiVersion> supportedVsdVersions = clientLoader.getNuageVspManagerClient().getSupportedVersionList();
                 supportedVsdVersions.retainAll(Arrays.asList(NuageVspApiVersion.SUPPORTED_VERSIONS));
diff --git a/plugins/network-elements/nuage-vsp/src/com/cloud/network/resource/NuageVspResource.java b/plugins/network-elements/nuage-vsp/src/com/cloud/network/resource/NuageVspResource.java
index 72ff5b016b4..502b4bda7f2 100644
--- a/plugins/network-elements/nuage-vsp/src/com/cloud/network/resource/NuageVspResource.java
+++ b/plugins/network-elements/nuage-vsp/src/com/cloud/network/resource/NuageVspResource.java
@@ -248,7 +248,7 @@ public Answer executeRequest(final Command cmd) {
             return wrapper.execute(cmd, this);
         } catch (final Exception e) {
             if (s_logger.isDebugEnabled()) {
-                s_logger.debug("Received unsupported command " + cmd.toString());
+                s_logger.debug("Received unsupported command " + cmd.toString(), e);
             }
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
diff --git a/plugins/network-elements/nuage-vsp/src/com/cloud/network/vsp/resource/wrapper/NuageVspExtraDhcpOptionsCommandWrapper.java b/plugins/network-elements/nuage-vsp/src/com/cloud/network/vsp/resource/wrapper/NuageVspExtraDhcpOptionsCommandWrapper.java
new file mode 100644
index 00000000000..7bd7e7c31f4
--- /dev/null
+++ b/plugins/network-elements/nuage-vsp/src/com/cloud/network/vsp/resource/wrapper/NuageVspExtraDhcpOptionsCommandWrapper.java
@@ -0,0 +1,42 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.network.vsp.resource.wrapper;
+
+import net.nuage.vsp.acs.client.exception.NuageVspException;
+
+import javax.naming.ConfigurationException;
+
+import com.cloud.agent.api.element.ExtraDhcpOptionsVspCommand;
+import com.cloud.network.resource.NuageVspResource;
+import com.cloud.resource.ResourceWrapper;
+
+@ResourceWrapper(handles=ExtraDhcpOptionsVspCommand.class)
+public class NuageVspExtraDhcpOptionsCommandWrapper extends NuageVspCommandWrapper<ExtraDhcpOptionsVspCommand> {
+    @Override
+    public boolean executeNuageVspCommand(ExtraDhcpOptionsVspCommand command, NuageVspResource nuageVspResource) throws ConfigurationException, NuageVspException {
+        nuageVspResource.getNuageVspElementClient().setDhcpOptionsNic(command.getNetwork(), command.getNicUuid(), command.getDhcpOptions());
+        return true;
+    }
+
+    @Override
+    public StringBuilder fillDetail(StringBuilder stringBuilder, ExtraDhcpOptionsVspCommand command) {
+        return stringBuilder;
+    }
+}
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 0081b9d209a..3325be1069c 100644
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -162,18 +162,21 @@
 import com.cloud.utils.Pair;
 import com.cloud.utils.StringUtils;
 import com.cloud.utils.db.EntityManager;
+import com.cloud.utils.net.Dhcp;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.Ip;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.ConsoleProxyVO;
 import com.cloud.vm.InstanceGroup;
 import com.cloud.vm.Nic;
+import com.cloud.vm.NicExtraDhcpOptionVO;
 import com.cloud.vm.NicProfile;
 import com.cloud.vm.NicSecondaryIp;
 import com.cloud.vm.NicVO;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.Type;
+import com.cloud.vm.dao.NicExtraDhcpOptionDao;
 import com.cloud.vm.dao.NicSecondaryIpVO;
 import com.cloud.vm.snapshot.VMSnapshot;
 import org.apache.cloudstack.acl.ControlledEntity;
@@ -233,6 +236,7 @@
 import org.apache.cloudstack.api.response.NetworkACLResponse;
 import org.apache.cloudstack.api.response.NetworkOfferingResponse;
 import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.NicExtraDhcpOptionResponse;
 import org.apache.cloudstack.api.response.NicResponse;
 import org.apache.cloudstack.api.response.NicSecondaryIpResponse;
 import org.apache.cloudstack.api.response.OvsProviderResponse;
@@ -317,6 +321,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.TimeZone;
+import java.util.stream.Collectors;
 
 public class ApiResponseHelper implements ResponseGenerator {
 
@@ -349,6 +354,8 @@
     private ClusterDetailsDao _clusterDetailsDao;
     @Inject
     private ResourceTagDao _resourceTagDao;
+    @Inject
+    private NicExtraDhcpOptionDao _nicExtraDhcpOptionDao;
 
     @Override
     public UserResponse createUserResponse(User user) {
@@ -3583,6 +3590,7 @@ public NicResponse createNicResponse(Nic result) {
         NetworkVO network = _entityMgr.findById(NetworkVO.class, result.getNetworkId());
         VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, result.getInstanceId());
         UserVmJoinVO userVm = _entityMgr.findById(UserVmJoinVO.class, result.getInstanceId());
+        List<NicExtraDhcpOptionVO> nicExtraDhcpOptionVOs = _nicExtraDhcpOptionDao.listByNicId(result.getId());
 
         response.setId(result.getUuid());
         response.setNetworkid(network.getUuid());
@@ -3601,6 +3609,13 @@ public NicResponse createNicResponse(Nic result) {
         }
         response.setIpaddress(result.getIPv4Address());
 
+        List<NicExtraDhcpOptionResponse> nicExtraDhcpOptionResponses = nicExtraDhcpOptionVOs
+                .stream()
+                .map(vo -> new NicExtraDhcpOptionResponse(Dhcp.DhcpOptionCode.valueOfInt(vo.getCode()).getName(), vo.getCode(), vo.getValue()))
+                .collect(Collectors.toList());
+
+        response.setExtraDhcpOptions(nicExtraDhcpOptionResponses);
+
         if (result.getSecondaryIp()) {
             List<NicSecondaryIpVO> secondaryIps = ApiDBUtils.findNicSecondaryIps(result.getId());
             if (secondaryIps != null) {
diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
index 75b42c21979..c0eb222be16 100644
--- a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
@@ -24,12 +24,14 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
 import org.apache.cloudstack.affinity.AffinityGroupResponse;
 import org.apache.cloudstack.api.ApiConstants.VMDetails;
 import org.apache.cloudstack.api.ResponseObject.ResponseView;
+import org.apache.cloudstack.api.response.NicExtraDhcpOptionResponse;
 import org.apache.cloudstack.api.response.NicResponse;
 import org.apache.cloudstack.api.response.NicSecondaryIpResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
@@ -49,9 +51,11 @@
 import com.cloud.uservm.UserVm;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.net.Dhcp;
 import com.cloud.vm.UserVmDetailVO;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.VmStats;
+import com.cloud.vm.dao.NicExtraDhcpOptionDao;
 import com.cloud.vm.dao.NicSecondaryIpVO;
 import com.cloud.vm.dao.UserVmDetailsDao;
 
@@ -67,6 +71,8 @@
     private UserVmDetailsDao _userVmDetailsDao;
     @Inject
     private UserDao _userDao;
+    @Inject
+    private NicExtraDhcpOptionDao _nicExtraDhcpOptionDao;
 
     private final SearchBuilder<UserVmJoinVO> VmDetailSearch;
     private final SearchBuilder<UserVmJoinVO> activeVmByIsoSearch;
@@ -267,6 +273,13 @@ public UserVmResponse newUserVmResponse(ResponseView view, String objectName, Us
                     nicResponse.setSecondaryIps(ipList);
                 }
                 nicResponse.setObjectName("nic");
+
+                List<NicExtraDhcpOptionResponse> nicExtraDhcpOptionResponses = _nicExtraDhcpOptionDao.listByNicId(nic_id)
+                        .stream()
+                        .map(vo -> new NicExtraDhcpOptionResponse(Dhcp.DhcpOptionCode.valueOfInt(vo.getCode()).getName(), vo.getCode(), vo.getValue()))
+                        .collect(Collectors.toList());
+                nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses);
+
                 userVmResponse.addNic(nicResponse);
             }
         }
@@ -368,6 +381,11 @@ public UserVmResponse setUserVmResponse(ResponseView view, UserVmResponse userVm
             }
 
             nicResponse.setObjectName("nic");
+            List<NicExtraDhcpOptionResponse> nicExtraDhcpOptionResponses = _nicExtraDhcpOptionDao.listByNicId(nic_id)
+                    .stream()
+                    .map(vo -> new NicExtraDhcpOptionResponse(Dhcp.DhcpOptionCode.valueOfInt(vo.getCode()).getName(), vo.getCode(), vo.getValue()))
+                    .collect(Collectors.toList());
+            nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses);
             userVmData.addNic(nicResponse);
         }
 
diff --git a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java
index d9113607772..9d3944de29a 100644
--- a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java
+++ b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java
@@ -1325,18 +1325,18 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) {
                 vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" +
                     getCurrentTimeStampString(),
                     "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null,
-                    null, true, null, null, null, null);
+                    null, true, null, null, null, null, null);
             } else {
                 if (zone.isSecurityGroupEnabled()) {
                     vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, null, null,
                         owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(),
                         "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null,
-                        null, null, true, null, null, null, null);
+                        null, null, true, null, null, null, null, null);
 
                 } else {
                     vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" +
                         getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(),
-                        null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null);
+                        null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null);
 
                 }
             }
diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java
index 9021999da3f..896a4892920 100644
--- a/server/src/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/com/cloud/network/element/VirtualRouterElement.java
@@ -930,6 +930,11 @@ public boolean removeDhcpSupportForSubnet(final Network network) throws Resource
         return removeDhcpSupportForSubnet(network, Service.Dhcp);
     }
 
+    @Override
+    public boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions) {
+        return false;
+    }
+
     @Override
     public boolean removeDnsSupportForSubnet(Network network) throws ResourceUnavailableException {
         // Ignore if virtual router is already dhcp provider
diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java
index 6a384f1162f..6ffc28e4403 100644
--- a/server/src/com/cloud/vm/UserVmManager.java
+++ b/server/src/com/cloud/vm/UserVmManager.java
@@ -106,7 +106,7 @@ boolean upgradeVirtualMachine(Long id, Long serviceOfferingId, Map<String, Strin
     boolean setupVmForPvlan(boolean add, Long hostId, NicProfile nic);
 
     UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, Boolean isDisplayVmEnabled, Long osTypeId, String userData,
-                                Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List<Long> securityGroupIdList) throws ResourceUnavailableException, InsufficientCapacityException;
+                                Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List<Long> securityGroupIdList, Map<String, Map<Integer, String>> extraDhcpOptionsMap) throws ResourceUnavailableException, InsufficientCapacityException;
 
     //the validateCustomParameters, save and remove CustomOfferingDetils functions can be removed from the interface once we can
     //find a common place for all the scaling and upgrading code of both user and systemvms.
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 791ad952fbd..f5365eb71f7 100644
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -32,10 +32,15 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import org.apache.cloudstack.affinity.AffinityGroupService;
@@ -89,9 +94,6 @@
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
@@ -296,6 +298,7 @@
 import com.cloud.vm.dao.InstanceGroupDao;
 import com.cloud.vm.dao.InstanceGroupVMMapDao;
 import com.cloud.vm.dao.NicDao;
+import com.cloud.vm.dao.NicExtraDhcpOptionDao;
 import com.cloud.vm.dao.SecondaryStorageVmDao;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
@@ -496,6 +499,8 @@
     protected IpAddressManager _ipAddrMgr;
     @Inject
     private SnapshotApiService _snapshotService;
+    @Inject
+    NicExtraDhcpOptionDao _nicExtraDhcpOptionDao;
 
     protected ScheduledExecutorService _executor = null;
     protected ScheduledExecutorService _vmIpFetchExecutor = null;
@@ -1161,10 +1166,10 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV
         }
 
         if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
-        if (!(network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Domain)
-                && !(network.getAclType() == ACLType.Account && network.getAccountId() == vmInstance.getAccountId())) {
-            throw new InvalidParameterValueException("only shared network or isolated network with the same account_id can be added to vmId: " + vmId);
-        }
+            if (!(network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Domain)
+                    && !(network.getAclType() == ACLType.Account && network.getAccountId() == vmInstance.getAccountId())) {
+                throw new InvalidParameterValueException("only shared network or isolated network with the same account_id can be added to vmId: " + vmId);
+            }
         }
 
         List<NicVO> allNics = _nicDao.listByVmId(vmInstance.getId());
@@ -1220,6 +1225,8 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV
 
         try {
             guestNic = _itMgr.addVmToNetwork(vmInstance, network, profile);
+            saveExtraDhcpOptions(guestNic.getId(), cmd.getDhcpOptionsMap());
+            _networkMgr.configureExtraDhcpOptions(network, guestNic.getId(), cmd.getDhcpOptionsMap());
             cleanUp = false;
         } catch (ResourceUnavailableException e) {
             throw new CloudRuntimeException("Unable to add NIC to " + vmInstance + ": " + e);
@@ -1245,6 +1252,17 @@ public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterV
         return _vmDao.findById(vmInstance.getId());
     }
 
+
+    private void saveExtraDhcpOptions(long nicId, Map<Integer, String> dhcpOptions) {
+        List<NicExtraDhcpOptionVO> nicExtraDhcpOptionVOList = dhcpOptions
+                .entrySet()
+                .stream()
+                .map(entry -> new NicExtraDhcpOptionVO(nicId, entry.getKey(), entry.getValue()))
+                .collect(Collectors.toList());
+
+        _nicExtraDhcpOptionDao.saveExtraDhcpOptions(nicExtraDhcpOptionVOList);
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_NIC_DELETE, eventDescription = "Removing Nic", async = true)
     public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException {
@@ -2417,7 +2435,7 @@ else if (details != null && !details.isEmpty()) {
         }
 
         return updateVirtualMachine(id, displayName, group, ha, isDisplayVm, osTypeId, userData, isDynamicallyScalable,
-                cmd.getHttpMethod(), cmd.getCustomId(), hostName, cmd.getInstanceName(), securityGroupIdList);
+                cmd.getHttpMethod(), cmd.getCustomId(), hostName, cmd.getInstanceName(), securityGroupIdList, cmd.getDhcpOptionsMap());
     }
 
     private void saveUsageEvent(UserVmVO vm) {
@@ -2467,7 +2485,7 @@ private void generateNetworkUsageForVm(VirtualMachine vm, boolean isDisplay, Str
 
     @Override
     public UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, Boolean isDisplayVmEnabled, Long osTypeId, String userData,
-            Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List<Long> securityGroupIdList)
+            Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List<Long> securityGroupIdList, Map<String, Map<Integer, String>> extraDhcpOptionsMap)
                     throws ResourceUnavailableException, InsufficientCapacityException {
         UserVmVO vm = _vmDao.findById(id);
         if (vm == null) {
@@ -2567,7 +2585,7 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo
                 }
             }
         }
-
+        List<? extends Nic> nics = _nicDao.listByVmId(vm.getId());
         if (hostName != null) {
             // Check is hostName is RFC compliant
             checkNameForRFCCompliance(hostName);
@@ -2578,14 +2596,27 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo
             }
 
             // Verify that vm's hostName is unique
-            List<NetworkVO> vmNtwks = new ArrayList<NetworkVO>();
-            List<? extends Nic> nics = _nicDao.listByVmId(vm.getId());
+
+            List<NetworkVO> vmNtwks = new ArrayList<NetworkVO>(nics.size());
             for (Nic nic : nics) {
                 vmNtwks.add(_networkDao.findById(nic.getNetworkId()));
             }
             checkIfHostNameUniqueInNtwkDomain(hostName, vmNtwks);
         }
 
+        List<NetworkVO> networks = nics.stream()
+                .map(nic -> _networkDao.findById(nic.getNetworkId()))
+                .collect(Collectors.toList());
+
+        verifyExtraDhcpOptionsNetwork(extraDhcpOptionsMap, networks);
+        for (Nic nic : nics) {
+            _networkMgr.saveExtraDhcpOptions(networks.stream()
+                    .filter(network -> network.getId() == nic.getNetworkId())
+                    .findFirst()
+                    .get()
+                    .getUuid(), nic.getId(), extraDhcpOptionsMap);
+        }
+
         _vmDao.updateVM(id, displayName, ha, osTypeId, userData, isDisplayVmEnabled, isDynamicallyScalable, customId, hostName, instanceName);
 
         if (updateUserdata) {
@@ -2911,7 +2942,7 @@ protected boolean validPassword(String password) {
     public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList,
             Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod,
             String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
-            Map<String, String> customParametes, String customId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
+            Map<String, String> customParametes, String customId, Map<String, Map<Integer, String>> dhcpOptionMap) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
             StorageUnavailableException, ResourceAllocationException {
 
         Account caller = CallContext.current().getCallingAccount();
@@ -2959,7 +2990,7 @@ public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOff
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod,
-                userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId);
+                userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId, dhcpOptionMap);
 
     }
 
@@ -2968,7 +2999,7 @@ public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOff
     public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
             List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
             HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
-            List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId) throws InsufficientCapacityException, ConcurrentOperationException,
+            List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap) throws InsufficientCapacityException, ConcurrentOperationException,
             ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
 
         Account caller = CallContext.current().getCallingAccount();
@@ -3070,7 +3101,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod,
-                userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId);
+                userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap);
     }
 
     @Override
@@ -3078,7 +3109,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service
     public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner,
             String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
             String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayvm, String keyboard, List<Long> affinityGroupIdList,
-            Map<String, String> customParametrs, String customId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
+            Map<String, String> customParametrs, String customId, Map<String, Map<Integer, String>> dhcpOptionsMap) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
             StorageUnavailableException, ResourceAllocationException {
 
         Account caller = CallContext.current().getCallingAccount();
@@ -3171,9 +3202,28 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv
                 networkList.add(network);
             }
         }
+        verifyExtraDhcpOptionsNetwork(dhcpOptionsMap, networkList);
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, httpmethod, userData,
-                sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId);
+                sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId, dhcpOptionsMap);
+    }
+
+    private void verifyExtraDhcpOptionsNetwork(Map<String, Map<Integer, String>> dhcpOptionsMap, List<NetworkVO> networkList) throws InvalidParameterValueException {
+        if (dhcpOptionsMap != null) {
+            for (String networkUuid : dhcpOptionsMap.keySet()) {
+                boolean networkFound = false;
+                for (NetworkVO network : networkList) {
+                    if (network.getUuid().equals(networkUuid)) {
+                        networkFound = true;
+                        break;
+                    }
+                }
+
+                if (!networkFound) {
+                    throw new InvalidParameterValueException("VM does not has a nic in the Network (" + networkUuid + ") that is specified in the extra dhcp options.");
+                }
+            }
+        }
     }
 
     public void checkNameForRFCCompliance(String name) {
@@ -3187,7 +3237,7 @@ public void checkNameForRFCCompliance(String name) {
     protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate tmplt, String hostName, String displayName, Account owner,
             Long diskOfferingId, Long diskSize, List<NetworkVO> networkList, List<Long> securityGroupIdList, String group, HTTPMethod httpmethod, String userData,
             String sshKeyPair, HypervisorType hypervisor, Account caller, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean isDisplayVm, String keyboard,
-            List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId) throws InsufficientCapacityException, ResourceUnavailableException,
+            List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap) throws InsufficientCapacityException, ResourceUnavailableException,
             ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException {
 
         _accountMgr.checkAccess(caller, null, true, owner);
@@ -3520,7 +3570,7 @@ protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOf
         }
 
         UserVmVO vm = commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, caller, isDisplayVm, keyboard, accountId, userId, offering,
-                isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters);
+                isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, dhcpOptionMap);
 
         // Assign instance to the group
         try {
@@ -3580,7 +3630,7 @@ private String generateHostName(String uuidName) {
     private UserVmVO commitUserVm(final DataCenter zone, final VirtualMachineTemplate template, final String hostName, final String displayName, final Account owner,
                                   final Long diskOfferingId, final Long diskSize, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard,
                                   final long accountId, final long userId, final ServiceOfferingVO offering, final boolean isIso, final String sshPublicKey, final LinkedHashMap<String, NicProfile> networkNicMap,
-                                  final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map<String, String> customParameters) throws InsufficientCapacityException {
+                                  final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map<String, String> customParameters, final Map<String, Map<Integer, String>> extraDhcpOptionMap) throws InsufficientCapacityException {
         return Transaction.execute(new TransactionCallbackWithException<UserVmVO, InsufficientCapacityException>() {
             @Override
             public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException {
@@ -3686,10 +3736,10 @@ public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCap
                 if (isIso) {
                     _orchSrvc.createVirtualMachineFromScratch(vm.getUuid(), Long.toString(owner.getAccountId()), vm.getIsoId().toString(), hostName, displayName,
                             hypervisorType.name(), guestOSCategory.getName(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags,
-                            networkNicMap, plan);
+                            networkNicMap, plan, extraDhcpOptionMap);
                 } else {
                     _orchSrvc.createVirtualMachine(vm.getUuid(), Long.toString(owner.getAccountId()), Long.toString(template.getId()), hostName, displayName, hypervisorType.name(),
-                            offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, rootDiskSize);
+                            offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, rootDiskSize, extraDhcpOptionMap);
                 }
 
                 if (s_logger.isDebugEnabled()) {
@@ -4634,7 +4684,7 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE
 
         Long templateId = cmd.getTemplateId();
 
-        if(!serviceOffering.isDynamic()) {
+        if (!serviceOffering.isDynamic()) {
             for(String detail: cmd.getDetails().keySet()) {
                 if(detail.equalsIgnoreCase(VmDetailConstants.CPU_NUMBER) || detail.equalsIgnoreCase(VmDetailConstants.CPU_SPEED) || detail.equalsIgnoreCase(VmDetailConstants.MEMORY)) {
                     throw new InvalidParameterValueException("cpuNumber or cpuSpeed or memory should not be specified for static service offering");
@@ -4685,13 +4735,13 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE
             } else {
                 vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd), owner, name, displayName, diskOfferingId,
                         size , group , cmd.getHypervisor(), cmd.getHttpMethod(), userData , sshKeyPairName , cmd.getIpToNetworkMap(), addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(),
-                        cmd.getDetails(), cmd.getCustomId());
+                        cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap());
             }
         } else {
             if (zone.isSecurityGroupEnabled())  {
                 vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, cmd.getNetworkIds(), getSecurityGroupIdList(cmd), owner, name,
                         displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard,
-                        cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId());
+                        cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap());
 
             } else {
                 if (cmd.getSecurityGroupIdList() != null && !cmd.getSecurityGroupIdList().isEmpty()) {
@@ -4699,7 +4749,7 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE
                 }
                 vm = createAdvancedVirtualMachine(zone, serviceOffering, template, cmd.getNetworkIds(), owner, name, displayName, diskOfferingId, size, group,
                         cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(),
-                        cmd.getCustomId());
+                        cmd.getCustomId(), cmd.getDhcpOptionsMap());
             }
         }
         return vm;
@@ -5546,7 +5596,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
 
             VirtualMachine vmi = _itMgr.findById(vm.getId());
             VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmi);
-            _networkMgr.allocate(vmProfile, networks);
+            _networkMgr.allocate(vmProfile, networks, null);
 
             _securityGroupMgr.addInstanceToGroups(vm.getId(), securityGroupIdList);
 
@@ -5668,7 +5718,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
                 if (applicableNetworks.isEmpty()) {
                     throw new InvalidParameterValueException("No network is specified, please specify one when you move the vm. For now, please add a network to VM on NICs tab.");
                 } else {
-                    _networkMgr.allocate(vmProfile, networks);
+                    _networkMgr.allocate(vmProfile, networks, null);
                 }
 
                 _securityGroupMgr.addInstanceToGroups(vm.getId(),
@@ -5796,7 +5846,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
                 }
                 VirtualMachine vmi = _itMgr.findById(vm.getId());
                 VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmi);
-                _networkMgr.allocate(vmProfile, networks);
+                _networkMgr.allocate(vmProfile, networks, null);
                 s_logger.debug("AssignVM: Advance virtual, adding networks no " + networks.size() + " to " + vm.getInstanceName());
             } // END IF NON SEC GRP ENABLED
         } // END IF ADVANCED
diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
index 9b895c25541..81f06bccfae 100644
--- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
@@ -523,11 +523,20 @@ public Network createPrivateNetwork(String networkName, String displayText, long
      * @see com.cloud.network.NetworkManager#allocate(com.cloud.vm.VirtualMachineProfile, java.util.List)
      */
     @Override
-    public void allocate(VirtualMachineProfile vm, LinkedHashMap<? extends Network, List<? extends NicProfile>> networks)
+    public void allocate(VirtualMachineProfile vm, LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, final Map<String, Map<Integer, String>> extraDhcpOptions)
             throws InsufficientCapacityException, ConcurrentOperationException {
         // TODO Auto-generated method stub
     }
 
+    @Override
+    public void configureExtraDhcpOptions(Network network, long nicId, Map<Integer, String> extraDhcpOptions) {
+
+    }
+
+    @Override public void configureExtraDhcpOptions(Network network, long nicId) {
+
+    }
+
     /* (non-Javadoc)
      * @see com.cloud.network.NetworkManager#prepare(com.cloud.vm.VirtualMachineProfile, com.cloud.deploy.DeployDestination, com.cloud.vm.ReservationContext)
      */
@@ -589,6 +598,11 @@ public void expungeNics(VirtualMachineProfile vm) {
         return null;
     }
 
+    @Override
+    public Map<Integer, String> getExtraDhcpOptions(long nicId) {
+        return null;
+    }
+
     /* (non-Javadoc)
      * @see com.cloud.network.NetworkManager#shutdownNetwork(long, com.cloud.vm.ReservationContext, boolean)
      */
@@ -653,6 +667,11 @@ public boolean reallocate(VirtualMachineProfile vm, DataCenterDeployment dest) t
         return false;
     }
 
+    @Override
+    public void saveExtraDhcpOptions(String networkUuid, Long nicId, Map<String, Map<Integer, String>> extraDhcpOptionMap) {
+
+    }
+
     /* (non-Javadoc)
      * @see com.cloud.network.NetworkManager#allocateNic(com.cloud.vm.NicProfile, com.cloud.network.Network, java.lang.Boolean, int, com.cloud.vm.VirtualMachineProfile)
      */
diff --git a/setup/db/db/schema-41000to41100.sql b/setup/db/db/schema-41000to41100.sql
index dbf798a92e6..0b099d6ef2e 100644
--- a/setup/db/db/schema-41000to41100.sql
+++ b/setup/db/db/schema-41000to41100.sql
@@ -449,3 +449,14 @@ CREATE VIEW `cloud`.`volume_view` AS
         `cloud`.`account` resource_tag_account ON resource_tag_account.id = resource_tags.account_id
             left join
         `cloud`.`domain` resource_tag_domain ON resource_tag_domain.id = resource_tags.domain_id;
+
+-- Extra Dhcp Options
+CREATE TABLE `cloud`.`nic_extra_dhcp_options` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
+  `uuid` varchar(255) UNIQUE,
+  `nic_id` bigint unsigned NOT NULL COMMENT ' nic id where dhcp options are applied',
+  `code` int(32),
+  `value` text,
+  PRIMARY KEY (`id`),
+  CONSTRAINT `fk_nic_extra_dhcp_options_nic_id` FOREIGN KEY (`nic_id`) REFERENCES `nics`(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/test/integration/plugins/nuagevsp/nuageTestCase.py b/test/integration/plugins/nuagevsp/nuageTestCase.py
index 9ab4726d95f..121921b3f82 100644
--- a/test/integration/plugins/nuagevsp/nuageTestCase.py
+++ b/test/integration/plugins/nuagevsp/nuageTestCase.py
@@ -78,6 +78,35 @@ def _wrapper(*args, **kwargs):
         return _wrapper
 
 
+class gherkin(object):
+    BLACK = "\033[0;30m"
+    BLUE = "\033[0;34m"
+    GREEN = "\033[0;32m"
+    CYAN = "\033[0;36m"
+    RED = "\033[0;31m"
+    BOLDBLUE = "\033[1;34m"
+    NORMAL = "\033[0m"
+
+    def __init__(self, method):
+        self.method = method
+
+    def __get__(self, obj=None, objtype=None):
+        @functools.wraps(self.method)
+        def _wrapper(*args, **kwargs):
+            gherkin_step = self.method.__name__.replace("_", " ").capitalize()
+            obj.info("=G= %s%s%s" % (self.BOLDBLUE, gherkin_step, self.NORMAL))
+            try:
+                result = self.method(obj, *args, **kwargs)
+                obj.info("=G= %s%s: [SUCCESS]%s" %
+                         (self.GREEN, gherkin_step, self.NORMAL))
+                return result
+            except Exception as e:
+                obj.info("=G= %s%s: [FAILED]%s%s" %
+                         (self.RED, gherkin_step, self.NORMAL, e))
+                raise
+        return _wrapper
+
+
 class nuageTestCase(cloudstackTestCase):
 
     @classmethod
diff --git a/test/integration/plugins/nuagevsp/test_nuage_extra_dhcp.py b/test/integration/plugins/nuagevsp/test_nuage_extra_dhcp.py
new file mode 100644
index 00000000000..cc52e66d7b1
--- /dev/null
+++ b/test/integration/plugins/nuagevsp/test_nuage_extra_dhcp.py
@@ -0,0 +1,2037 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+""" Component tests for extra dhcp options functionality with
+Nuage VSP SDN plugin
+"""
+# Import Local Modules
+from nuageTestCase import (nuageTestCase, gherkin)
+from marvin.cloudstackAPI import updateVirtualMachine, updateZone
+from marvin.lib.base import (Account,
+                             Network,
+                             VirtualMachine,
+                             Configurations,
+                             NetworkOffering)
+# Import System Modules
+from concurrent.futures import ThreadPoolExecutor, wait
+from nose.plugins.attrib import attr
+import copy
+import time
+
+
+class TestNuageExtraDhcp(nuageTestCase):
+    """ Test basic VPC Network functionality with Nuage VSP SDN plugin
+    """
+
+    @classmethod
+    def setUpClass(cls, zone=None):
+        super(TestNuageExtraDhcp, cls).setUpClass()
+        cls.vmdata = cls.test_data["virtual_machine"]
+        cls.sharednetworkdata = cls.test_data["acl"]
+
+        # Create an account
+        cls.account = Account.create(cls.api_client,
+                                     cls.test_data["account"],
+                                     admin=True,
+                                     domainid=cls.domain.id
+                                     )
+
+        cmd = updateZone.updateZoneCmd()
+        cmd.id = cls.zone.id
+        cmd.domain = "testvpc.com"
+        cls.api_client.updateZone(cmd)
+        cls.vpc_offering = cls.create_VpcOffering(
+            cls.test_data["nuagevsp"]["vpc_offering_nuage_dhcp"])
+        cls.vpc1 = cls.create_Vpc(cls.vpc_offering, cidr="10.0.0.0/16",
+                                  networkDomain="testvpc.com")
+
+        cls.vpc_network_offering = cls.create_NetworkOffering(
+            cls.test_data["nuagevsp"]["vpc_network_offering_nuage_dhcp"])
+
+        cls.vpc_network = cls.create_Network(
+            cls.vpc_network_offering, gateway="10.0.0.1", vpc=cls.vpc1)
+
+        cmd.domain = "testisolated.com"
+        cls.api_client.updateZone(cmd)
+
+        # create the isolated network
+        cls.isolated_network_offering = cls.create_NetworkOffering(
+            cls.test_data["nuagevsp"]["isolated_network_offering"], True)
+        cls.isolated_network = cls.create_Network(
+            cls.isolated_network_offering, gateway="10.0.0.1",
+            netmask="255.255.255.0")
+
+        cmd.domain = "testshared.com"
+        cls.api_client.updateZone(cmd)
+        # Create Shared Network
+        cls.shared_network_offering = NetworkOffering.create(
+            cls.api_client,
+            cls.test_data["nuagevsp"]["shared_nuage_network_offering"],
+            conservemode=False
+        )
+
+        cls.shared_network_offering.update(cls.api_client, state='Enabled')
+        cls.shared_network_offering_id = cls.shared_network_offering.id
+
+        cls.shared_network_all = Network.create(
+            cls.api_client,
+            cls.test_data["nuagevsp"]["network_all"],
+            networkofferingid=cls.shared_network_offering_id,
+            zoneid=cls.zone.id
+        )
+        cls.dhcp_options_map = {}
+        cls.dhcp_options_to_verify_map = {}
+        cls.expected_dhcp_options_on_vm = {}
+        cls.dhcp_options_map_keys = [1, 16, 28, 41, 64, 93]
+
+        cls._cleanup = [
+            cls.shared_network_all,
+            cls.shared_network_offering,
+            cls.account
+            ]
+        return
+
+    def setUp(self):
+        self.vmdata["displayname"] = "vm"
+        self.vmdata["name"] = "vm"
+        self.update_NuageVspGlobalDomainTemplateName(name="")
+        self.dhcp_options_map.update({1: {"dhcp:1": "255.255.255.0",
+                                          "dhcp:2": "10",
+                                          "dhcp:4": "10.0.0.2,"
+                                                    "10.0.0.3,"
+                                                    "10.0.0.4",
+                                          "dhcp:7": "10.0.0.5,10.0.0.6",
+                                          "dhcp:9": "10.0.0.7",
+                                          "dhcp:13": "255",
+                                          }})
+        self.dhcp_options_map.update({16: {"dhcp:16": "10.0.0.8",
+                                           "dhcp:17": "/tmp/",
+                                           "dhcp:18": "/ext/",
+                                           "dhcp:19": "1",
+                                           "non-local-source-routing": "0",
+                                           "policy-filter": "10.1.2.12,"
+                                                            "255.255.255.0",
+                                           "max-datagram-reassembly": "1000",
+                                           "default-ttl": "255",
+                                           "mtu": "1024",
+                                           "all-subnets-local": "1"}})
+        self.dhcp_options_map.update({28: {"broadcast": "10.1.2.255",
+                                           "router-discovery": "0",
+                                           "router-solicitation": "10.1.2.2",
+                                           "static-route": "10.0.0.1,10.0.0.2",
+                                           "trailer-encapsulation": "1",
+                                           "arp-timeout": "255",
+                                           "ethernet-encap": "1",
+                                           "tcp-ttl": "255",
+                                           "tcp-keepalive": "255",
+                                           "nis-domain": "www.test.com"}})
+        self.dhcp_options_map.update({41: {"nis-server": "10.1.1.1,10.1.1.2",
+                                           "ntp-server": "10.1.1.3,10.1.1.4",
+                                           "netbios-ns": "10.1.1.5,10.1.1.6",
+                                           "netbios-dd": "10.1.1.8,10.1.1.7",
+                                           "netbios-nodetype": "08",
+                                           "netbios-scope": "test2",
+                                           "x-windows-fs": "10.1.2.13,"
+                                                           "10.1.2.16",
+                                           "x-windows-dm": "10.1.2.14,"
+                                                           "10.1.2.15",
+                                           "requested-address": "10.1.2.16",
+                                           "vendor-class": "01"}})
+        self.dhcp_options_map.update({64: {"nis+-domain": "www.test3.com",
+                                           "nis+-server": "10.1.2.255,"
+                                                          "10.1.2.254",
+                                           "tftp-server": "www.test4.com",
+                                           "bootfile-name": "file.txt",
+                                           "mobile-ip-home": "10.1.2.18,"
+                                                             "10.1.2.19",
+                                           "smtp-server": "10.1.2.20,"
+                                                          "10.1.2.21",
+                                           "pop3-server": "10.1.2.2,10.1.2.3",
+                                           "nntp-server": "10.1.2.5,10.1.2.4",
+                                           "irc-server": "10.1.2.1,10.1.2.4",
+                                           "user-class": "user-class"}})
+        self.dhcp_options_map.update({93: {"client-arch": "16",
+                                           "client-interface-id": "01",
+                                           "client-machine-id": "01",
+                                           "dhcp:114":
+                                               "http://www.testdhcpfeature."
+                                               "com/adfsgbfgtdhh125ki-23-fdh"
+                                               "-09",
+                                           "domain-search": "www.domain.com",
+                                           "sip-server": "www.sip.com",
+                                           "classless-static-route":
+                                               "10.1.0.0/16,10.1.0.1",
+                                           "vendor-id-encap":
+                                               "0000ACEF04CAFEBABE"
+                                           }})
+        self.dhcp_options_to_verify_map.update({1: {1: "255.255.255.0",
+                                                2: "10",
+                                                4: ["10.0.0.2",
+                                                    "10.0.0.3",
+                                                    "10.0.0.4"],
+                                                7: ["10.0.0.5",
+                                                    "10.0.0.6"],
+                                                9: "10.0.0.7",
+                                                13: "255"}})
+        self.dhcp_options_to_verify_map.update({16: {16: "10.0.0.8",
+                                                     17: "/tmp/",
+                                                     18: "/ext/",
+                                                     19: "1",
+                                                     20: "0",
+                                                     21: ["10.1.2.12",
+                                                          "255.255.255.0"],
+                                                     22: "1000",
+                                                     23: "255",
+                                                     26: "1024",
+                                                     27: "1"}})
+        self.dhcp_options_to_verify_map.update({28: {28: "10.1.2.255",
+                                                     31: "0",
+                                                     32: "10.1.2.2",
+                                                     33: ["10.0.0.1",
+                                                          "10.0.0.2"],
+                                                     34: "1",
+                                                     35: "255",
+                                                     36: "1",
+                                                     37: "255",
+                                                     38: "255",
+                                                     40: "www.test.com"}})
+        self.dhcp_options_to_verify_map.update({41: {41: ["10.1.1.1",
+                                                          "10.1.1.2"],
+                                                     42: ["10.1.1.3",
+                                                          "10.1.1.4"],
+                                                     44: ["10.1.1.5",
+                                                          "10.1.1.6"],
+                                                     45: ["10.1.1.8",
+                                                          "10.1.1.7"],
+                                                     46: "H-node",
+                                                     47: "test2",
+                                                     48: ["10.1.2.13",
+                                                          "10.1.2.16"],
+                                                     49: ["10.1.2.14",
+                                                          "10.1.2.15"],
+                                                     50: "10.1.2.16",
+                                                     60: "01"}})
+        self.dhcp_options_to_verify_map.update({64: {64: "www.test3.com",
+                                                     65: ["10.1.2.255",
+                                                          "10.1.2.254"],
+                                                     66: "www.test4.com",
+                                                     67: "file.txt",
+                                                     68: ["10.1.2.18",
+                                                          "10.1.2.19"],
+                                                     69: ["10.1.2.20",
+                                                          "10.1.2.21"],
+                                                     70: ["10.1.2.2",
+                                                          "10.1.2.3"],
+                                                     71: ["10.1.2.5",
+                                                          "10.1.2.4"],
+                                                     74: ["10.1.2.1",
+                                                          "10.1.2.4"],
+                                                     77: "user-class"}})
+        self.dhcp_options_to_verify_map.update({93: {93: "16",
+                                                     94: "01",
+                                                     97: "01",
+                                                     114: "http://www.testdhcp"
+                                                          "feature.com/adfsgbf"
+                                                          "gtdhh125ki-23-fdh-"
+                                                          "09",
+                                                     119: "www.domain.com",
+                                                     120: "www.sip.com",
+                                                     121: ["10.1.0.0/16",
+                                                           "10.1.0.1"],
+                                                     125: "0000acef04cafebabe"
+                                                     }})
+        self.expected_dhcp_options_on_vm.update({46: "netbios-node-type 8",
+                                                 60: "vendor-class-identifier"
+                                                     " 1",
+                                                 94: "unknown-94 1",
+                                                 93: "unknown-93",
+                                                 97: "unknown-97 1",
+                                                 119: "unknown-119",
+                                                 120: "unknown-120",
+                                                 121: "unknown-121",
+                                                 125: "0:0:ac:ef:4:ca:fe:ba:be"
+                                                 })
+        self.cleanup = []
+        return
+
+    def tearDown(self):
+        super(TestNuageExtraDhcp, self).tearDown()
+        # Cleanup resources used
+        self.debug("Cleaning up the resources")
+        self.update_NuageVspGlobalDomainTemplateName(name="")
+        for obj in reversed(self.cleanup):
+            try:
+                if isinstance(obj, VirtualMachine):
+                    obj.delete(self.api_client, expunge=True)
+                else:
+                    obj.delete(self.api_client)
+            except Exception as e:
+                self.error("Failed to cleanup %s, got %s" % (obj, e))
+        # cleanup_resources(self.api_client, self.cleanup)
+        self.cleanup = []
+        self.debug("Cleanup complete!")
+        return
+
+    def retrieve_dhcp_values_to_verify_on_vm_based_on(self, dhcp_map):
+        vm_dhcp_map = copy.deepcopy(dhcp_map)
+        for dhcpcode, dhcpval in self.expected_dhcp_options_on_vm.iteritems():
+            if dhcpcode in dhcp_map:
+                vm_dhcp_map[dhcpcode] = dhcpval
+
+        return vm_dhcp_map
+
+    def verify_vsd_dhcp_option_subnet(self, dhcp_type, value, subnet):
+        self.debug("Verifying the creation and value of DHCP option type -"
+                   " %s in VSD" % dhcp_type)
+        found_dhcp_type = False
+        dhcp_options = self.vsd.get_subnet_dhcpoptions(
+            filter=self.get_externalID_filter(subnet.id))
+        for dhcp_option in dhcp_options:
+            self.debug("dhcptype option in vsd is : %s"
+                       % dhcp_option.actual_type)
+            self.debug("dhcptype expected value is: %s" % value)
+            if dhcp_option.actual_type == dhcp_type:
+                found_dhcp_type = True
+                if isinstance(dhcp_option.actual_values, list):
+                    self.debug("dhcptype actual value on vsd is %s:"
+                               "" % dhcp_option.actual_values)
+                    if value in dhcp_option.actual_values:
+                        self.debug("Excepted DHCP option value found in"
+                                   " VSD")
+                    else:
+                        self.fail("Excepted DHCP option value not found"
+                                  " in VSD")
+                else:
+                    self.debug("dhcptype actual value on vsd is %s:"
+                               % dhcp_option.actual_values)
+                    self.assertEqual(dhcp_option.actual_values, value,
+                                     "Expected DHCP option value is not same"
+                                     " in both CloudStack and VSD")
+        if not found_dhcp_type:
+            self.fail("Expected DHCP option type and value not found"
+                      " in the VSD")
+        self.debug("Successfully verified the creation and value of DHCP"
+                   " option type - %s in VSD" % dhcp_type)
+
+    def verify_vsd_dhcp_option(self, dhcp_type, value, vm_interface):
+        self.debug("Verifying the creation and value of DHCP option type -"
+                   " %s in VSD" % dhcp_type)
+        self.debug("Expected value for this dhcp option is - %s in VSD"
+                   % value)
+        found_dhcp_type = False
+        dhcp_options = self.vsd.get_vm_interface_dhcpoptions(
+            filter=self.get_externalID_filter(vm_interface.id))
+        for dhcp_option in dhcp_options:
+            self.debug("dhcptype on vsd is %s:" % dhcp_option.actual_type)
+            self.debug("dhcp value on vsd is: %s:" % dhcp_option.actual_values)
+            if dhcp_option.actual_type == dhcp_type:
+                found_dhcp_type = True
+                if isinstance(dhcp_option.actual_values, list):
+                    self.debug("dhcptype actual value is %s:" %
+                               dhcp_option.actual_values)
+                    if type(value) is list:
+                        for val in value:
+                            self.is_value_in_options(dhcp_option.actual_values,
+                                                     val)
+                    else:
+                        self.is_value_in_options(dhcp_option.actual_values,
+                                                 value)
+                else:
+                    self.assertEqual(dhcp_option.actual_values, value,
+                                     "Expected DHCP option value is not same"
+                                     " in both CloudStack and VSD")
+        if not found_dhcp_type:
+            self.fail("Expected DHCP option type and value not found in "
+                      "the VSD for dhcp type %s " % dhcp_type)
+        self.debug("Successfully verified the creation and value of DHCP"
+                   " option type - %s in VSD" % dhcp_type)
+
+    def is_value_in_options(self, actual_options, value):
+        if value in actual_options:
+            self.debug("Excepted DHCP option value found in VSD")
+        else:
+            self.fail("Excepted DHCP option value not found in VSD")
+
+    def verify_vsd_dhcp_option_empty(self, dhcp_type, vm_interface):
+        self.debug("Verifying the creation and value of DHCP option"
+                   " type - %s in VSD" % dhcp_type)
+        self.debug("Expected value is empty string")
+        dhcp_options = self.vsd.get_vm_interface_dhcpoptions(
+            filter=self.get_externalID_filter(vm_interface.id))
+        for dhcp_option in dhcp_options:
+            self.debug("dhcptype on vsd is %s:" % dhcp_option.actual_type)
+            self.debug("dhcp value on vsd is: %s:" % dhcp_option.value)
+            if dhcp_option.actual_type == dhcp_type:
+                if dhcp_type == 15:
+                    self.assertEqual(dhcp_option.value, "\x00",
+                                     "Expected DHCP option value is not"
+                                     " same in both CloudStack and VSD")
+                else:
+                    self.assertEqual(dhcp_option.value, "00",
+                                     "Expected DHCP option value is not"
+                                     " same in both CloudStack and VSD")
+        self.debug("Successfully verified the creation and value of"
+                   " DHCP option type - %s in VSD" % dhcp_type)
+
+    def verify_vsd_dhcp_value_notpresent(self, value, vm_interface):
+        self.debug("Verifying that on vminterface value is not present- %s"
+                   % value)
+        dhcp_options = self.vsd.get_vm_interface_dhcpoptions(
+            filter=self.get_externalID_filter(vm_interface.id))
+        for dhcp_option in dhcp_options:
+            self.debug("dhcptype option is %s:" % dhcp_option.actual_type)
+            if isinstance(dhcp_option.actual_values, list):
+                self.debug("dhcptype actual value is %s:"
+                           % dhcp_option.actual_values)
+                if value in dhcp_option.actual_values:
+                    self.fail("This value is not expected on vminterface but "
+                              "present as dhcp_type %s"
+                              % dhcp_option.actual_type)
+                else:
+                    self.debug("As Excepted DHCP value not found in VSD")
+            else:
+                try:
+                    self.assertEqual(dhcp_option.actual_values, value,
+                                     "Expected DHCP option value is not same "
+                                     "in both CloudStack and VSD")
+                    self.fail("This value is not expected on vm interface but "
+                              "present as dhcp_type %s"
+                              % dhcp_option.actual_type)
+                except Exception:
+                    self.debug("As Expected DHCP value not found in VSD")
+        self.debug("Successfully verified dhcp value is not present - %s "
+                   "in VSD" % value)
+
+    def verify_vsd_dhcp_type_notpresent(self, dhcp_types, vm_interface):
+        if type(dhcp_types) is not list:
+            dhcp_types = [dhcp_types]
+
+        for dhcp_type in dhcp_types:
+            self.debug("Verifying that DHCP option type - %s not present"
+                       " in VSD" % dhcp_type)
+            dhcp_options = self.vsd.get_vm_interface_dhcpoptions(
+                filter=self.get_externalID_filter(vm_interface.id))
+            for dhcp_option in dhcp_options:
+                self.debug("dhcptype on vsd is %s:" % dhcp_option.actual_type)
+                if dhcp_option.actual_type == dhcp_type:
+                    self.fail("Expected DHCP option type is not expected in "
+                              "the VSD: %s" % dhcp_type)
+            self.debug("Successfully verified DHCP option type - %s "
+                       "not present in the VSD" % dhcp_type)
+
+    def verify_dhcp_on_vm(
+            self, dhcpleasefile, dhcp_option_map, ssh_client, cleanlease=True):
+        cmd = 'cat /var/lib/dhclient/'+dhcpleasefile
+        self.debug("get content of dhcp lease file " + cmd)
+        outputlist = ssh_client.execute(cmd)
+        self.debug("command is executed properly " + cmd)
+        completeoutput = str(outputlist).strip('[]')
+        self.debug("complete output is " + completeoutput)
+
+        for key, value in dhcp_option_map.iteritems():
+            if type(value) is list:
+                for val in value:
+                    self.check_if_value_contains(completeoutput, val)
+            else:
+                self.check_if_value_contains(completeoutput, value)
+        if cleanlease:
+            self.remove_lease_file(ssh_client, dhcpleasefile)
+
+    def check_if_value_contains(self, completeoutput, value):
+        if value in completeoutput:
+            self.debug("excepted value found in vm: " + value)
+        else:
+            self.fail("excepted value not found in vm: " + value)
+
+    def remove_lease_file(self, ssh_client, dhcpleasefile):
+        cmd = 'rm -rf /var/lib/dhclient/'+dhcpleasefile
+        outputlist = ssh_client.execute(cmd)
+        completeoutput = str(outputlist).strip('[]')
+        self.debug("clear lease is done properly:" + completeoutput)
+
+    def update_zone_details(self, value):
+        """Updates the VM data"""
+        # update Network Domain at zone level
+        cmd = updateZone.updateZoneCmd()
+        cmd.id = self.zone.id
+        cmd.domain = value
+        self.api_client.updateZone(cmd)
+
+    def update_NuageVspGlobalDomainTemplateName(self, name):
+        self.debug("Updating global setting nuagevsp.vpc.domaintemplate.name "
+                   "with value - %s" % name)
+        Configurations.update(self.api_client,
+                              name="nuagevsp.vpc.domaintemplate.name",
+                              value=name)
+        self.debug("Successfully updated global setting "
+                   "nuagevsp.vpc.domaintemplate.name with value - %s" % name)
+
+    def create_isolated_network(
+            self, network_offering=None, gateway="10.1.1.1",
+            netmask="255.255.255.0"):
+        # create a isolated network
+        self.debug("Creating an Isolated network...")
+        if not network_offering:
+            network_offering = self.create_isolated_network_offering()
+
+        network = self.create_Network(network_offering, gateway, netmask)
+
+        return network
+
+    def validate_isolated_network(
+            self, network_offering, network):
+        self.debug("Validating network...")
+        self.validate_NetworkOffering(network_offering, state="Enabled")
+        self.validate_Network(network)
+
+    def validate_vpc(self, vpc, vpc_offering):
+        self.debug("Validating vpc...")
+        self.validate_Vpc(vpc)
+        self.validate_VpcOffering(vpc_offering)
+
+    def verify_dhcp_options_on_vm(
+            self, vm, network, vpc, dhcp_options, remove_lease_file=True,
+            lease_file="dhclient-eth0.leases", verify_on_vsd=True):
+        # Adding Ingress Firewall/Network ACL rule
+        self.debug("Adding Ingress Firewall/Network ACL rule to make the "
+                   "created Static NAT rule (wget) accessible...")
+        public_ip = self.acquire_PublicIPAddress(network, vpc=vpc)
+        self.create_StaticNatRule_For_VM(
+                vm, public_ip, network)
+
+        if vpc:
+            public_http_rule = self.create_NetworkAclRule(
+                self.test_data["ingress_rule"], network=network)
+        else:
+            public_http_rule = self.create_FirewallRule(public_ip)
+
+        # VSD verification
+        if verify_on_vsd:
+            self.verify_vsd_firewall_rule(public_http_rule)
+
+        ssh_client = self.ssh_into_VM(vm, public_ip)
+        dhcp_options_to_verify_on_vm =\
+            self.retrieve_dhcp_values_to_verify_on_vm_based_on(
+                dhcp_options)
+        self.verify_dhcp_on_vm(
+            lease_file, dhcp_options_to_verify_on_vm,
+            ssh_client, remove_lease_file)
+
+        # Removing Ingress Firewall/Network ACL rule
+        self.debug("Removing the created Ingress Firewall/Network ACL "
+                   "rule in the network...")
+        public_http_rule.delete(self.api_client)
+
+        public_ip.delete(self.api_client)
+        with self.assertRaises(Exception):
+            self.validate_PublicIPAddress(public_ip, network)
+
+    def verify_dhcp_options_on_vsd(
+            self, vm, dhcp_options,
+            networks_with_options=None,
+            verify_vm_on_vsd=True):
+
+        if verify_vm_on_vsd:
+            self.verify_vsd_vm(vm)
+
+        if networks_with_options:
+            for nic in vm.nic:
+                if self.is_nic_in_network_list(nic, networks_with_options):
+                    for key, value in dhcp_options.iteritems():
+                        self.verify_vsd_dhcp_option(key, value, nic)
+        else:
+            for nic in vm.nic:
+                for key, value in dhcp_options.iteritems():
+                    self.verify_vsd_dhcp_option(key, value, nic)
+
+    @staticmethod
+    def is_nic_in_network_list(nic, network_list):
+        if type(network_list) is list:
+            for network in network_list:
+                if network.id == nic.networkid:
+                    return True
+            return False
+        elif network_list.id == nic.networkid:
+            return True
+        return False
+
+    def validate_network_on_vsd_based_on_networktype(
+            self, network, vpc=None, is_shared_network=False):
+        if is_shared_network:
+            self.verify_vsd_shared_network(
+                self.domain.id, network,
+                gateway=self.test_data["nuagevsp"]["network_all"]["gateway"])
+        else:
+            self.verify_vsd_network(self.domain.id, network, vpc)
+
+    def create_vpc_offering_with_nuage_dhcp(self):
+        # Creating a VPC offering
+        self.debug("Creating Nuage VSP VPC offering without dhcp...")
+        vpc_offering = self.create_VpcOffering(
+            self.test_data["nuagevsp"]["vpc_offering_nuage_dhcp"])
+        self.validate_VpcOffering(vpc_offering, state="Enabled")
+
+        return vpc_offering
+
+    def create_isolated_network_offering(self):
+        network_offering = self.create_NetworkOffering(
+            self.test_data["nuagevsp"]["isolated_network_offering"])
+        self.validate_NetworkOffering(network_offering, state="Enabled")
+        return network_offering
+
+    def create_vpc_network_offering(self):
+        network_offering = self.create_NetworkOffering(
+            self.test_data["nuagevsp"]["vpc_network_offering_nuage_dhcp"])
+        self.validate_NetworkOffering(network_offering, state="Enabled")
+        return network_offering
+
+    def create_vpc(self, vpc_offering, cidr="10.0.0.0/16"):
+        # Creating a VPC
+        self.debug("Creating a VPC with Nuage VSP VPC offering...")
+        vpc = self.create_Vpc(vpc_offering, cidr=cidr,
+                              networkDomain="testvpc.com")
+        self.validate_Vpc(vpc, state="Enabled")
+
+        return vpc
+
+    def create_vpc_with_tier(self, domain_name="testvpc.com"):
+        vpc_offering = self.create_vpc_offering_with_nuage_dhcp()
+        vpc = self.create_vpc(vpc_offering)
+
+        vpc_network_offering = self.create_vpc_network_offering()
+        acl_list = self.create_acl_list_with_item(vpc)
+        vpc_first_tier = \
+            self.when_i_create_a_first_vpc_network_with_nuage_dhcp(
+                vpc, vpc_network_offering, acl_list)
+
+        self.verify_vsd_dhcp_option_subnet(15, domain_name, vpc_first_tier)
+
+        return {"vpc": vpc, "tier": vpc_first_tier}
+
+    def create_acl_list_with_item(self, vpc):
+        # Creating an ACL list
+        acl_list = self.create_NetworkAclList(name="acl", description="acl",
+                                              vpc=vpc)
+
+        # Creating an ACL item
+        self.create_NetworkAclRule(self.test_data["ingress_rule"],
+                                   acl_list=acl_list)
+
+        return acl_list
+
+    @staticmethod
+    def add_extra_dhcp_options_to_check(dhcp_options_to_verify, domain_name,
+                                        remove_dns_options=False):
+        if not remove_dns_options:
+            dhcp_options_to_verify[12] = "vm1"
+            dhcp_options_to_verify[15] = domain_name
+        return dhcp_options_to_verify
+
+    def get_extra_dhcp_options_starting_with(self, dhcp_option_code,
+                                             network=None):
+        dhcp_options =\
+            copy.deepcopy(self.dhcp_options_map.get(dhcp_option_code))
+        if network:
+            dhcp_options["networkid"] = network.id
+        return dhcp_options
+
+    def get_extra_dhcp_options_to_verify_starting_with(
+            self, number, domain_name, remove_dns_options=False):
+        dhcp_options_to_verify = copy.deepcopy(
+            self.dhcp_options_to_verify_map.get(number))
+        self.add_extra_dhcp_options_to_check(
+            dhcp_options_to_verify, domain_name, remove_dns_options)
+        return dhcp_options_to_verify
+
+    @gherkin
+    def when_i_update_the_zone_details_and_restart_a_vpc(self, vpc):
+        self.update_zone_details("testvpc.com")
+        vpc.restart(self.api_client)
+
+    @gherkin
+    def when_i_create_a_first_vpc_network_with_nuage_dhcp(
+            self, vpc, network_offering, acl_list, gateway="10.0.0.1"):
+        # Creating a VPC network in the VPC
+        self.debug(
+            "Creating a VPC network with Nuage VSP VPC Network offering...")
+        vpc_network = self.create_Network(network_offering, gateway=gateway,
+                                          vpc=vpc, acl_list=acl_list)
+        self.validate_Network(vpc_network, state="Implemented")
+
+        return vpc_network
+
+    @gherkin
+    def when_i_create_a_second_vpc_network_with_nuage_dhcp(
+            self, vpc, network_offering, acl_list):
+        vpc_network_1 = self.create_Network(
+            network_offering, gateway='10.1.2.1', vpc=vpc, acl_list=acl_list)
+        self.validate_Network(vpc_network_1, state="Implemented")
+
+        return vpc_network_1
+
+    @gherkin
+    def when_i_stop_and_start_a_vm(self, vm):
+        vm.stop(self.api_client)
+        vm.start(self.api_client)
+
+    @gherkin
+    def when_i_add_an_extra_nic_to_a_vm(self, vm, network, dhcp_options=None):
+        dhcp_options_list = None
+
+        if dhcp_options:
+            if type(dhcp_options) is list:
+                dhcp_options_list = []
+                for item in dhcp_options:
+                    dhcp_options_list.extend([item])
+            else:
+                dhcp_options_list = [dhcp_options]
+
+        return vm.add_nic(self.api_client, network.id,
+                          dhcpoptions=dhcp_options_list)
+
+    @gherkin
+    def when_i_restart_a_network(self, network):
+        network.restart(self.api_client, cleanup=True)
+
+    @gherkin
+    def when_i_create_a_vm(
+            self, network, vpc, vm_name, dhcp_options,
+            start_vm=True, is_shared_network=False,
+            ip_address=None):
+        vm_data = copy.deepcopy(self.vmdata)
+        if dhcp_options:
+            if type(dhcp_options) is list:
+                dhcp_options_list = []
+                for item in dhcp_options:
+                    dhcp_options_list.extend([item])
+            else:
+                dhcp_options_list = [dhcp_options]
+
+            vm_data["dhcpoptionsnetworklist"] = dhcp_options_list
+        elif "dhcpoptionsnetworklist" in vm_data:
+            del vm_data["dhcpoptionsnetworklist"]
+
+        if ip_address:
+            vm_data["ipaddress"] = ip_address
+        if vm_name:
+            vm_data["displayname"] = vm_name
+            vm_data["name"] = vm_name
+
+        vm = self.create_VM(network, start_vm=start_vm, testdata=vm_data)
+        if start_vm:
+            self.check_VM_state(vm, state="Running")
+        else:
+            self.check_VM_state(vm, state="Stopped")
+        # VSD verification
+        if type(network) is not list:
+            self.validate_network_on_vsd_based_on_networktype(
+                network, vpc, is_shared_network)
+
+        return vm
+
+    @gherkin
+    def when_i_update_extra_dhcp_options_on_a_vm(self, vm, dhcp_options):
+        """Updates the VM data"""
+        if type(dhcp_options) is list:
+            dhcp_options_list = []
+            for item in dhcp_options:
+                dhcp_options_list.extend([item])
+        else:
+            dhcp_options_list = [dhcp_options]
+
+        cmd = updateVirtualMachine.updateVirtualMachineCmd()
+        cmd.id = vm.id
+        cmd.dhcpoptionsnetworklist = dhcp_options_list
+        self.api_client.updateVirtualMachine(cmd)
+
+    @gherkin
+    def then_verify_domain_name_and_router_options_multi_nic_set(
+            self, multinic_vm, primary_network, domain_name="testvpc.com"):
+        for nic in multinic_vm.nic:
+            if nic.networkid != primary_network.id:
+                self.verify_vsd_dhcp_option(3, "0.0.0.0", nic)
+                self.verify_vsd_dhcp_option(15, "\x00", nic)
+            else:
+                self.verify_vsd_dhcp_option(15, domain_name, nic)
+
+    @gherkin
+    def then_verify_dhcp_options_on_vsd_and_vm(self, vm, network,
+                                               dhcp_options_to_verify,
+                                               network_with_options=None,
+                                               is_shared_network=False,
+                                               verify_on_vm=False,
+                                               default_network=None,
+                                               vpc=None,
+                                               remove_lease_file=False,
+                                               verify_on_vsd=True):
+
+        if verify_on_vsd:
+            self.verify_dhcp_options_on_vsd(
+                vm, dhcp_options_to_verify, network_with_options,
+                not is_shared_network)
+        if verify_on_vm and not self.isSimulator and not is_shared_network:
+            if default_network:
+                network = default_network
+                lease_file = "dhclient-eth1.leases"
+            else:
+                lease_file = "dhclient-eth0.leases"
+
+            self.verify_dhcp_options_on_vm(
+                vm=vm,
+                network=network,
+                vpc=vpc,
+                dhcp_options=dhcp_options_to_verify,
+                lease_file=lease_file,
+                remove_lease_file=remove_lease_file,
+                verify_on_vsd=verify_on_vsd)
+
+    @gherkin
+    def then_no_dhcp_options_present_on_vsd(self, dhcp_options_map, vm,
+                                            excluded_nics=None):
+        for nic in vm.nic:
+            if not excluded_nics or nic not in excluded_nics:
+                self.verify_vsd_dhcp_type_notpresent(
+                    dhcp_options_map.keys(), nic)
+                self.verify_vsd_dhcp_value_notpresent(
+                    dhcp_options_map.values(), nic)
+
+    def validate_all_extra_dhcp_for_add_remove_nic_after_migrate(
+            self, network, domain_name="testisolated.com",
+            is_shared_network=False, verify_all_options=False):
+        # 1 - create an extra isolated network
+        # 2 - for each extra dhc option:
+            # a - deploy a vm
+            # b - migrate vm
+            # c - plug nic
+            # d - verify the dhcp options are correctly set on the nic
+            # e - remove nic
+            # f - verify the dhcp options are no longer present on the vsd
+            # and vm
+        # 3 - try to remove the default nic which has extra dhcp options set
+        # (this should fail)
+
+        isolated_network2 =\
+            self.create_isolated_network(gateway="10.0.1.1")
+
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            vm1 = self.when_i_create_a_vm(
+                isolated_network2, None, "vm1",
+                dhcp_options=None,
+                is_shared_network=False)
+
+            if not self.isSimulator:
+                self.migrate_VM(vm1)
+
+            result = self.when_i_add_an_extra_nic_to_a_vm(vm1, network, None)
+            dhcp_options_network = self.get_extra_dhcp_options_starting_with(
+                number, network)
+            self.when_i_update_extra_dhcp_options_on_a_vm(
+                vm1, dhcp_options_network)
+            self.when_i_stop_and_start_a_vm(vm1)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, remove_dns_options=True)
+
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                network_with_options=network,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                default_network=isolated_network2,
+                vpc=None)
+
+            vm1.remove_nic(
+                self.api_client,
+                [nic for nic in result.nic
+                 if nic.networkid == network.id][0].id)
+
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, remove_dns_options=True)
+            self.then_no_dhcp_options_present_on_vsd(dhcp_options_to_verify,
+                                                     vm1)
+            self.delete_VM(vm1)
+
+    def validate_vm_deploy_concurrency(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False):
+
+        old_dhcp_options =\
+            self.get_extra_dhcp_options_starting_with(1, network)
+        old_dhcp_options_to_verify =\
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                1, domain_name, True)
+
+        new_dhcp_options =\
+            self.get_extra_dhcp_options_starting_with(16, network)
+        new_dhcp_options_to_verify =\
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                16, domain_name, True)
+
+        def deploy_update_validate_vm(number):
+            vm = self.when_i_create_a_vm(
+                [network], vpc, "vm-%02d" % number,
+                old_dhcp_options,
+                is_shared_network=is_shared_network)
+
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm, network, old_dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc,
+                verify_on_vsd=False)
+
+            self.when_i_update_extra_dhcp_options_on_a_vm(vm, new_dhcp_options)
+            self.when_i_stop_and_start_a_vm(vm)
+
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm, network, new_dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc,
+                verify_on_vsd=False)
+
+            self.delete_VM(vm)
+
+        try:
+            executor = ThreadPoolExecutor(max_workers=10)
+
+            vm_futures = [executor.submit(
+                deploy_update_validate_vm, i)
+                    for i in range(10)]
+
+            wait(vm_futures)
+
+            [f.result()
+             for f in vm_futures]
+
+        finally:
+            executor.shutdown(wait=True)
+
+    def validate_all_extra_dhcp_for_network_actions_in_network(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False,
+            verify_all_options=False):
+        # 1 - for each extra dhcp option:
+            # a - deploy a vm with dhcp options
+            # b - restart the network
+            # c - check if the extra dhcp options are still correct
+            # d - restart the network with clean up = false
+            # e - check if the extra dhcp options are still correct
+            # f - if the network is a vpc, restart the vpc
+            # g - check if the extra dhcp options are still correct
+            # h - delete the vm
+            # i - create a vm
+            # j - stop the vm
+            # k - start the vm in a seperate thread
+            # l - add an extra nic while the vn is still in starting state
+            # m - delete the the vm
+        # 2 - deploy a vm
+        # 3 - wait for the network to go into allocated state
+        # 4 - deploy a new vm in the network
+        # 5 - check if all options are set correctly
+        # 6 - delete the network
+
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            dhcp_options_network =\
+                self.get_extra_dhcp_options_starting_with(number, network)
+            vm1 = self.when_i_create_a_vm(
+                network, vpc, "vm1", dhcp_options_network,
+                is_shared_network=is_shared_network)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, is_shared_network)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                vpc=vpc, remove_lease_file=False)
+
+            network.restart(self.api_client, True)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                vpc=vpc, remove_lease_file=False)
+
+            network.restart(self.api_client, False)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                vpc=vpc, remove_lease_file=False)
+
+            if vpc:
+                self.restart_Vpc(vpc)
+                self.then_verify_dhcp_options_on_vsd_and_vm(
+                    vm1, network, dhcp_options_to_verify,
+                    is_shared_network=is_shared_network, verify_on_vm=True,
+                    vpc=vpc, remove_lease_file=False)
+
+                self.restart_Vpc(vpc, True)
+                self.then_verify_dhcp_options_on_vsd_and_vm(
+                    vm1, network, dhcp_options_to_verify,
+                    is_shared_network=is_shared_network, verify_on_vm=True,
+                    vpc=vpc)
+
+            self.delete_VM(vm1)
+
+            dhcp_options_network = \
+                self.get_extra_dhcp_options_starting_with(number, network)
+
+            vm2 = self.when_i_create_a_vm(
+                network, vpc, "vm2", dhcp_options_network,
+                is_shared_network=is_shared_network)
+
+            isolated_network2 =\
+                self.create_isolated_network(gateway="10.0.1.1")
+
+            dhcp_options_network =\
+                self.get_extra_dhcp_options_starting_with(
+                    number, None)
+            vm_nic = self.when_i_add_an_extra_nic_to_a_vm(
+                vm2, isolated_network2, dhcp_options=dhcp_options_network)
+
+            dhcp_options_to_verify = \
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, remove_dns_options=True)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm2, isolated_network2, dhcp_options_to_verify,
+                is_shared_network=is_shared_network, verify_on_vm=False)
+
+            if not is_shared_network:
+                self.then_verify_domain_name_and_router_options_multi_nic_set(
+                    vm2, network, domain_name)
+
+            vm2.update_default_nic(
+                self.api_client,
+                [nic
+                 for nic in vm_nic.nic
+                    if not nic.isdefault][0].id)
+            self.when_i_stop_and_start_a_vm(vm2)
+
+            if not is_shared_network:
+                self.then_verify_domain_name_and_router_options_multi_nic_set(
+                    vm2, isolated_network2, domain_name)
+
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm2, isolated_network2, dhcp_options_to_verify,
+                is_shared_network=is_shared_network, verify_on_vm=True)
+            self.delete_VM(vm2)
+
+    def validate_all_extra_dhcp_for_network_in_allocated(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False):
+        dhcp_options_network =\
+            self.get_extra_dhcp_options_starting_with(1, network)
+        vm3 = self.when_i_create_a_vm(
+            network, vpc, "vm3", dhcp_options_network,
+            is_shared_network=is_shared_network)
+        vm3.stop(self.api_client)
+        # wait 1 min for network to go into allocated state
+        time.sleep(60)
+        dhcp_options_network =\
+            self.get_extra_dhcp_options_starting_with(64, network)
+        vm4 = self.when_i_create_a_vm(
+            network, vpc, "vm1", dhcp_options_network,
+            is_shared_network=is_shared_network)
+        dhcp_options_to_verify = \
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                64, domain_name, is_shared_network)
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm4, network, dhcp_options_to_verify,
+            is_shared_network=is_shared_network, verify_on_vm=True, vpc=vpc)
+
+        self.delete_VM(vm4)
+        self.delete_VM(vm3)
+        self.delete_Network(network)
+        if vpc:
+            vpc.delete(self.api_client)
+
+    def validate_all_extra_dhcp_for_vm_actions_in_network(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False,
+            verify_all_options=False):
+        # 1 - for each extra dhcp options:
+            # a - create a vm with dhcp options
+            # b - start and stop the vm
+            # c - check if the dhcp options are set correctly
+            # d - reboot the vm
+            # e - check if the dhcp options are set correctly
+            # f - delete a vm without expunging it
+            # g - recover the vm
+            # h - start the vm
+            # i - check if the dhcp options are set correctly
+            # j - delete the vm
+        # 2 - create a vm with extra dhcp options set
+        # 3 - check if the dhcp options are set correctly
+        # 4 - update the vm with new extra dhcp options
+        # 5 - reboot the vm
+        # 6 - verify the dhcp options on the vm and the vsd are not updated
+        # 7 - delete the vm
+
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            dhcp_options_network =\
+                self.get_extra_dhcp_options_starting_with(number, network)
+            vm1 = self.when_i_create_a_vm(
+                network, vpc, "vm1", dhcp_options_network,
+                is_shared_network=is_shared_network)
+
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, is_shared_network)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc)
+
+            self.when_i_stop_and_start_a_vm(vm1)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc)
+
+            vm1.reboot(self.api_client)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc)
+
+            vm1.delete(self.api_client, False)
+            vm1.recover(self.api_client)
+            vm1.start(self.api_client)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc)
+
+            vm1.restore(self.api_client)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc)
+
+            if not self.isSimulator:
+                self.migrate_VM(vm1)
+                self.then_verify_dhcp_options_on_vsd_and_vm(
+                    vm1, network, dhcp_options_to_verify,
+                    is_shared_network=is_shared_network, verify_on_vm=True,
+                    vpc=vpc)
+
+            vm1.delete(self.api_client, True)
+
+        dhcp_options_network = self.get_extra_dhcp_options_starting_with(
+            1, network)
+        vm1 = self.when_i_create_a_vm(
+            network, vpc, "vm1", dhcp_options_network,
+            is_shared_network=is_shared_network)
+        dhcp_options_to_verify =\
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                1, domain_name, is_shared_network)
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm1, network, dhcp_options_to_verify,
+            is_shared_network=is_shared_network,
+            verify_on_vm=True,
+            vpc=vpc)
+
+        dhcp_options_network_not_present =\
+            self.get_extra_dhcp_options_starting_with(93, network)
+        dhcp_options_to_verify_network_not_present =\
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                93, domain_name, True)
+        self.when_i_update_extra_dhcp_options_on_a_vm(
+            vm1, dhcp_options_network_not_present)
+        vm1.reboot(self.api_client)
+
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm1, network, dhcp_options_to_verify,
+            is_shared_network=is_shared_network,
+            verify_on_vm=True,
+            vpc=vpc)
+        self.then_no_dhcp_options_present_on_vsd(
+            dhcp_options_to_verify_network_not_present, vm1)
+
+        if not self.isSimulator:
+            self.migrate_VM(vm1)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc)
+
+        dhcp_options_network = self.get_extra_dhcp_options_starting_with(
+            64, network)
+        dhcp_options_to_verify = \
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                64, domain_name, is_shared_network)
+        self.when_i_update_extra_dhcp_options_on_a_vm(
+            vm1, dhcp_options_network)
+        vm1.restore(self.api_client)
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm1, network, dhcp_options_to_verify,
+            is_shared_network=is_shared_network,
+            verify_on_vm=True,
+            vpc=vpc)
+
+        self.delete_VM(vm1)
+
+    def validate_all_extra_dhcp_for_remove_nic_from_vm(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False,
+            verify_all_options=False):
+        # 1 - create an extra isolated network
+        # 2 - for each extra dhc option:
+            # a - deploy a vm
+            # b - plug nic
+            # c - verify the dhcp options are correctly set on the nic
+            # d - remove nic
+            # e - verify the dhcp options are no longer present on the vsd
+            # and vm
+        # 3 - try to remove the default nic which has extra dhcp options set
+        # (this should fail)
+
+        isolated_network2 =\
+            self.create_isolated_network(gateway="10.0.1.1")
+
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            vm1 = self.when_i_create_a_vm(
+                isolated_network2, None, "vm1", dhcp_options=None, is_shared_network=False)
+            result = self.when_i_add_an_extra_nic_to_a_vm(vm1, network, None)
+            dhcp_options_network = self.get_extra_dhcp_options_starting_with(
+                number, network)
+            self.when_i_update_extra_dhcp_options_on_a_vm(
+                vm1, dhcp_options_network)
+            self.when_i_stop_and_start_a_vm(vm1)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, True)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify=dhcp_options_to_verify,
+                network_with_options=network,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                default_network=isolated_network2, vpc=None)
+            vm1.remove_nic(
+                self.api_client,
+                [nic for nic in result.nic
+                 if nic.networkid == network.id][0].id)
+
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, True)
+            self.then_no_dhcp_options_present_on_vsd(dhcp_options_to_verify,
+                                                     vm1)
+            self.delete_VM(vm1)
+
+        # invalid remove option
+        vm1 = self.when_i_create_a_vm(
+            network, vpc, "vm1", None, is_shared_network=is_shared_network)
+        result = self.when_i_add_an_extra_nic_to_a_vm(
+            vm1, isolated_network2, None)
+        self.when_i_update_extra_dhcp_options_on_a_vm(
+            vm1, dhcp_options_network)
+        self.when_i_stop_and_start_a_vm(vm1)
+        with self.assertRaises(Exception):
+            vm1.remove_nic(
+                self.api_client, [nic for nic in result.nic
+                                  if nic.networkid == network.id][0])
+
+    def validate_all_extra_dhcp_for_update_multinic(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False,
+            verify_all_options=False):
+        # 1 - create an extra isolated network
+        # 2 - for each extra dhcp option:
+            # a - deploy a vm and ad an extra nic
+            # b - update the dhcp options on both nics
+            # c - verify the dhcp options are not yet set on the vsd and vm
+            # d - start and stop the vm
+            # e - verify the new dhcp options are set on the vsd and vm
+        # 3 - try to update a multi nic vm with invalid options
+        # (this should fail)
+
+        isolated_network2 =\
+            self.create_isolated_network(gateway="10.0.1.1")
+
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            vm1 = self.when_i_create_a_vm(
+                isolated_network2, None, "vm1",
+                dhcp_options=None,
+                is_shared_network=False)
+            self.when_i_add_an_extra_nic_to_a_vm(vm1, network, None)
+
+            dhcp_options_network = self.get_extra_dhcp_options_starting_with(
+                number, network)
+            dhcp_options_network2 = self.get_extra_dhcp_options_starting_with(
+                number, isolated_network2)
+            dhcp_options_list = [dhcp_options_network, dhcp_options_network2]
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, True)
+            self.when_i_update_extra_dhcp_options_on_a_vm(vm1,
+                                                          dhcp_options_list)
+            self.then_no_dhcp_options_present_on_vsd(dhcp_options_to_verify,
+                                                     vm1)
+
+            self.when_i_stop_and_start_a_vm(vm1)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, True)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify=dhcp_options_to_verify,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                default_network=isolated_network2, vpc=None)
+            self.delete_VM(vm1)
+
+        invalid_dhcp_options_list = [{"networkid": network.id,
+                                      "dhcp:":
+                                          "http://www.testdhcpfeature.com/"
+                                          "adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": network.id,
+                                      "dhcp:241":
+                                          "http://www.testdhcpfeature.com/"
+                                          "adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": network.id,
+                                      "unknownvalue":
+                                          "http://www.testdhcpfeature.com/"
+                                          "adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": "invalidnetworkid",
+                                      "dhcp:114":
+                                          "http://www.testdhcpfeature.com/"
+                                          "adfsgbfgtdhh125ki-23-fdh-09"}]
+        valid_dhcp_option = {"networkid": isolated_network2.id,
+                             "dhcp:124":
+                                 "http://www.testdhcpfeature.com/"
+                                 "adfsgbfgtdhh125ki-23-fdh-09"}
+        for invalid_dhcp_option in invalid_dhcp_options_list:
+            vm1 = self.when_i_create_a_vm(
+                isolated_network2, vpc, "vm1",
+                dhcp_options=None,
+                is_shared_network=False)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    93, domain_name, True)
+            self.then_no_dhcp_options_present_on_vsd(dhcp_options_to_verify,
+                                                     vm1)
+            combined_options = [invalid_dhcp_option, valid_dhcp_option]
+            with self.assertRaises(Exception):
+                self.when_i_update_extra_dhcp_options_on_a_vm(
+                    vm1, combined_options)
+            self.delete_VM(vm1)
+
+    def validate_all_extra_dhcp_for_multi_nic(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False,
+            verify_all_options=False):
+        # 1 - create an extra isolated network
+        # 2 - for each extra dhcp option:
+            # a - deploy a vm with a nic in two networks
+            # b - verify that the dhcp options are correctly set on the vsd
+            # and vn
+            # c - deploy a vm with a nic in two networks but now, let the other
+            # network be the default network of the vm
+            # d - verify that the dhcp options are correctly set on the vsd
+            # and vm
+        # 3 - try to deploy a multi nic vm with invalid dhcp options
+        # (should fail)
+
+        isolated_network2 =\
+            self.create_isolated_network(gateway="10.0.1.1")
+
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            dhcp_options_network = self.get_extra_dhcp_options_starting_with(
+                number, network)
+            dhcp_options_network2 = self.get_extra_dhcp_options_starting_with(
+                number, isolated_network2)
+            dhcp_options = [dhcp_options_network, dhcp_options_network2]
+
+            # default nic is the network provided
+            multinic_vm = self.when_i_create_a_vm(
+                [network, isolated_network2], vpc, "vm1", dhcp_options,
+                is_shared_network=is_shared_network)
+
+            if not is_shared_network:
+                self.then_verify_domain_name_and_router_options_multi_nic_set(
+                    multinic_vm, network, domain_name)
+
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, is_shared_network)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                multinic_vm, network, dhcp_options_to_verify,
+                network_with_options=network,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                vpc=vpc)
+
+            # is not primary nic so no option 12
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, remove_dns_options=True)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                multinic_vm, network, dhcp_options_to_verify,
+                network_with_options=isolated_network2,
+                is_shared_network=is_shared_network, verify_on_vm=False,
+                default_network=network, vpc=vpc)
+            self.delete_VM(multinic_vm)
+
+            # default nic is isolated_network2
+            multinic_vm = self.when_i_create_a_vm(
+                [isolated_network2, network], vpc, "vm1", dhcp_options,
+                is_shared_network=is_shared_network)
+
+            if not is_shared_network:
+                self.then_verify_domain_name_and_router_options_multi_nic_set(
+                    multinic_vm, isolated_network2, domain_name)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, is_shared_network)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                multinic_vm, network, dhcp_options_to_verify,
+                network_with_options=isolated_network2,
+                is_shared_network=is_shared_network, verify_on_vm=False,
+                vpc=vpc)
+
+            # is not primary nic so no option 12
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, True)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                multinic_vm, network,
+                dhcp_options_to_verify=dhcp_options_to_verify,
+                network_with_options=network,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                default_network=isolated_network2, vpc=None)
+            self.delete_VM(multinic_vm)
+
+        invalid_dhcp_options_list = [{"networkid": network.id,
+                                      "dhcp:":
+                                          "http://www.testdhcpfeature.com"
+                                          "/adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": network.id,
+                                      "dhcp:241":
+                                          "http://www.testdhcpfeature.com"
+                                          "/adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": network.id,
+                                      "unknownvalue":
+                                          "http://www.testdhcpfeature.com"
+                                          "/adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": "invalidnetworkid",
+                                      "dhcp:114":
+                                          "http://www.testdhcpfeature.com"
+                                          "/adfsgbfgtdhh125ki-23-fdh-09"}]
+        for invalid_dhcp_option in invalid_dhcp_options_list:
+            with self.assertRaises(Exception):
+                self.when_i_create_a_vm(
+                    [isolated_network2, network], vpc, "vm1",
+                    dhcp_options=invalid_dhcp_option,
+                    is_shared_network=is_shared_network)
+
+    def validate_all_extra_dhcp_after_plug_nic(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False,
+            verify_all_options=False):
+        # 1 - create an extra isolated network
+        # 2 - for each extra dchp options:
+            # a - deploy a vm in the created isolated network
+            # b - add an extra nic
+            # c - verify if the dhcp options are correctly set
+        # 3 - try to add a nic with invalid dhcp options (this should fail)
+
+        isolated_network2 =\
+            self.create_isolated_network(gateway="10.0.1.1")
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, True)
+            vm1 = self.when_i_create_a_vm(
+                isolated_network2, vpc, "vm1", None,
+                is_shared_network=False)
+
+            self.then_no_dhcp_options_present_on_vsd(
+                dhcp_options_to_verify, vm1)
+
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, True)
+
+            dhcp_options = self.get_extra_dhcp_options_starting_with(number)
+            self.when_i_add_an_extra_nic_to_a_vm(vm1, network,
+                                                 dhcp_options=dhcp_options)
+            vm1.reboot(self.api_client)
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify=dhcp_options_to_verify,
+                network_with_options=network,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                default_network=isolated_network2, vpc=None)
+            self.delete_VM(vm1)
+
+        invalid_dhcp_options_list = [
+            {"dhcp:": "http://www.testdhcpfeature.com/"
+                      "adfsgbfgtdhh125ki-23-fdh-09"},
+            {"dhcp:241": "http://www.testdhcpfeature.com/"
+                         "adfsgbfgtdhh125ki-23-fdh-09"},
+            {"unknownvalue": "http://www.testdhcpfeature.com/"
+                             "adfsgbfgtdhh125ki-23-fdh-09"}]
+        for invalid_dhcp_option in invalid_dhcp_options_list:
+            vm1 = self.when_i_create_a_vm(
+                isolated_network2, vpc, "vm1", None,
+                is_shared_network=False)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    93, domain_name, remove_dns_options=True)
+            self.then_no_dhcp_options_present_on_vsd(dhcp_options_to_verify,
+                                                     vm1)
+            with self.assertRaises(Exception):
+                self.when_i_add_an_extra_nic_to_a_vm(
+                    vm1, network, dhcp_options=invalid_dhcp_option)
+            self.delete_VM(vm1)
+
+    def validate_all_extra_dhcp_after_vm_update(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False,
+            verify_all_options=False):
+        # 1 - deploy a vm without extra dhcp options
+        # 2 - verify no dhcp options are present
+        # 3 - For each extra dhcp options
+            # a - update the vm with extra dhcp options
+            # b - check that the vm options are yet not updated on the vsd
+            # and vm
+            # c - stop and start the vm
+            # d - check that the dhcp options are set on the vsd an vm
+        # 4 - update a vm zith invalid dhcp options (this should fail)
+
+        # option 1 to 13 is special because we start a vm here
+        # instead of update
+        dhcp_options_to_verify =\
+            self.get_extra_dhcp_options_to_verify_starting_with(1, domain_name,
+                                                                True)
+        vm1 = self.when_i_create_a_vm(
+            network, vpc, "vm1", None,
+            is_shared_network=is_shared_network)
+        self.then_no_dhcp_options_present_on_vsd(dhcp_options_to_verify, vm1)
+
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            dhcp_options = self.get_extra_dhcp_options_starting_with(number,
+                                                                     network)
+            self.when_i_update_extra_dhcp_options_on_a_vm(vm1, dhcp_options)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, True)
+            self.then_no_dhcp_options_present_on_vsd(dhcp_options_to_verify,
+                                                     vm1)
+
+            # dhcp options get applied after start stop vm
+            self.when_i_stop_and_start_a_vm(vm1)
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, is_shared_network)
+
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network, verify_on_vm=True,
+                vpc=vpc)
+
+        invalid_dhcp_options_list = [{"networkid": network.id,
+                                      "dhcp:": "http://www.testdhcpfeature.com"
+                                               "/adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": network.id,
+                                      "dhcp:241": "http://www.testdhcpfeature"
+                                                  ".com/adfsgbfgtdhh125ki-23"
+                                                  "-fdh-09"},
+                                     {"networkid": network.id,
+                                      "unknownvalue": "http://www.testdhcp"
+                                                      "feature.com/"
+                                                      "adfsgbfgtdhh125ki-23-"
+                                                      "fdh-09"},
+                                     {"networkid": "invalidnetworkid",
+                                      "dhcp:114": "http://www.testdhcpfeature"
+                                                  ".com/adfsgbfgtdhh125ki-23-"
+                                                  "fdh-09"}]
+        for invalid_dhcp_option in invalid_dhcp_options_list:
+            with self.assertRaises(Exception):
+                self.when_i_update_extra_dhcp_options_on_a_vm(
+                    vm1, invalid_dhcp_option)
+
+    def validate_all_extra_dhcp_deploy_vm(
+            self, network,
+            vpc=None,
+            domain_name="testisolated.com",
+            is_shared_network=False,
+            verify_all_options=False):
+        # 1 - For each extra dhcp option:
+            # a - deploy a vm with extra dhcp options
+            # b - verify if the options are present on the vsd and vm
+            # c - delete the VM
+        # 2 - create a vm with different invalid dhcp options
+        # (this should fail)
+
+        if verify_all_options:
+            options_to_verify = self.dhcp_options_map_keys
+        else:
+            options_to_verify = [1]
+
+        for number in options_to_verify:
+            dhcp_options = self.get_extra_dhcp_options_starting_with(
+                number, network)
+
+            dhcp_options_to_verify =\
+                self.get_extra_dhcp_options_to_verify_starting_with(
+                    number, domain_name, is_shared_network)
+            vm1 = self.when_i_create_a_vm(
+                network, vpc, "vm1", dhcp_options,
+                is_shared_network=is_shared_network)
+
+            self.then_verify_dhcp_options_on_vsd_and_vm(
+                vm1, network, dhcp_options_to_verify,
+                is_shared_network=is_shared_network,
+                verify_on_vm=True,
+                vpc=vpc)
+            self.delete_VM(vm1)
+
+        invalid_dhcp_options_list = [{"networkid": network.id,
+                                      "dhcp:": "http://www.testdhcpfeature.com"
+                                               "/adfsgbfgtdhh125ki-23-fdh"
+                                               "-09"},
+                                     {"networkid": network.id,
+                                      "dhcp:241":
+                                          "http://www.testdhcpfeature.com"
+                                          "/adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": network.id,
+                                     "unknownvalue":
+                                         "http://www.testdhcpfeature"
+                                         ".com/adfsgbfgtdhh125ki-23-fdh-09"},
+                                     {"networkid": "invalidnetworkid",
+                                     "dhcp:114":
+                                         "http://www.testdhcpfeature.com"
+                                         "/adfsgbfgtdhh125ki-23-fdh-09"}]
+        for invalid_dhcp_option in invalid_dhcp_options_list:
+            with self.assertRaises(Exception):
+                self.when_i_create_a_vm(
+                    network, vpc, "vm2", invalid_dhcp_option,
+                    is_shared_network=is_shared_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_01_nuage_extra_dhcp_single_nic_in_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_deploy_vm(self.isolated_network,
+                                               verify_all_options=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_02_nuage_extra_dhcp_single_nic_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_deploy_vm(
+            self.vpc_network,
+            self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_03_nuage_extra_dhcp_single_nic_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_deploy_vm(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_04_nuage_extra_dhcp_update_vm_in_isoltated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_after_vm_update(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_05_nuage_extra_dhcp_update_vm_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_after_vm_update(
+            self.vpc_network, self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_06_nuage_extra_dhcp_update_vm_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_after_vm_update(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_07_nuage_extra_dhcp_add_nic_in_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_after_plug_nic(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_08_nuage_extra_dhcp_add_nic_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_after_plug_nic(
+            self.vpc_network, self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_09_nuage_extra_dhcp_add_nic_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_after_plug_nic(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_10_nuage_extra_dhcp_deploy_multi_nic_vm_in_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_for_multi_nic(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_11_nuage_extra_dhcp_deploy_multi_nic_vm_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_for_multi_nic(
+            self.vpc_network, self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_12_nuage_extra_dhcp_deploy_multi_nic_vm_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_for_multi_nic(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_13_nuage_extra_dhcp_update_multi_nic_in_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_for_update_multinic(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_14_nuage_extra_dhcp_update_multi_nic_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_for_update_multinic(
+            self.vpc_network, self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_15_nuage_extra_dhcp_update_multi_nic_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_for_update_multinic(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_16_nuage_extra_dhcp_remove_nic_in_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_for_remove_nic_from_vm(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_17_nuage_extra_dhcp_remove_nic_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_for_remove_nic_from_vm(
+            network=self.vpc_network,
+            vpc=self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_18_nuage_extra_dhcp_remove_nic_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_for_remove_nic_from_vm(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_19_nuage_extra_dhcp_vm_actions_in_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_for_vm_actions_in_network(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_20_nuage_nuage_extra_dhcp_vm_actions_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_for_vm_actions_in_network(
+            network=self.vpc_network,
+            vpc=self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_21_nuage_extra_dhcp_vm_actions_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_for_vm_actions_in_network(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_22_nuage_extra_dhcp_network_actions_in_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_for_network_actions_in_network(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_23_nuage_nuage_extra_dhcp_network_actions_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_for_network_actions_in_network(
+            self.vpc_network,
+            vpc=self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_24_nuage_extra_dhcp_network_actions_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_for_network_actions_in_network(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_25_nuage_extra_dhcp_nic_after_migrate_in_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network)
+        self.validate_all_extra_dhcp_for_add_remove_nic_after_migrate(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_26_nuage_nuage_extra_dhcp_nic_after_migrate_in_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_for_add_remove_nic_after_migrate(
+            self.vpc_network,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_27_nuage_extra_dhcp_nic_after_migrate_in_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_for_add_remove_nic_after_migrate(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_28_nuage_extra_dhcp_deploy_multiple_vms(self):
+        self.update_zone_details("testisolated.com")
+        isolated_network =\
+            self.create_isolated_network(gateway="10.0.0.1")
+        self.validate_vm_deploy_concurrency(
+            isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_29_nuage_extra_dhcp_allocated_isolated_network(self):
+        self.update_zone_details("testisolated.com")
+        self.validate_isolated_network(
+            self.isolated_network_offering, self.isolated_network,)
+        self.validate_all_extra_dhcp_for_network_in_allocated(
+            self.isolated_network)
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_30_nuage_extra_dhcp_allocated_vpc(self):
+        self.update_zone_details("testvpc.com")
+        self.validate_vpc(self.vpc1, self.vpc_offering)
+        self.validate_Network(self.vpc_network)
+
+        self.validate_all_extra_dhcp_for_network_in_allocated(
+            self.vpc_network, self.vpc1,
+            domain_name="testvpc.com")
+
+    @attr(tags=["advanced", "nuagevsp"], required_hardware="false")
+    def test_31_nuage_extra_dhcp_allocated_shared_network(self):
+        self.update_zone_details("testshared.com")
+        self.validate_all_extra_dhcp_for_network_in_allocated(
+            self.shared_network_all,
+            domain_name="testshared.com",
+            is_shared_network=True)
+
+    @attr(tags=["advanced", "nuagevsp", "smoke"], required_hardware="false")
+    def smoke_test(self):
+        # This test does basic sanity checks to see if basic
+        # DHCP options still work.
+        # 1 - deploy vm in an isolated network
+        # 2 - verify dhcp options
+        # 3 - update dhcp options
+        # 4 - add nic to a vpc_network with different dhcp options
+        # 5 - restart the vm
+        # 6 - check if dhcp options are on the extra nic and the default nic
+        # 7 - restart the network
+        # 8 - verify if the dhcp options are set correctly
+        # 9 - remove the vm
+
+        network = self.isolated_network
+        domain_name = "testisolated.com"
+        self.update_zone_details(domain_name)
+
+        dhcp_options_isolated_network =\
+            self.get_extra_dhcp_options_starting_with(1, network)
+
+        dhcp_options_to_verify = \
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                1, domain_name, remove_dns_options=False)
+        vm1 = self.when_i_create_a_vm(
+            network,
+            vpc=None,
+            vm_name="vm1",
+            dhcp_options=dhcp_options_isolated_network)
+
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm1, network, dhcp_options_to_verify,
+            verify_on_vm=True)
+
+        dhcp_options_isolated_network =\
+            self.get_extra_dhcp_options_starting_with(16, network)
+        self.when_i_update_extra_dhcp_options_on_a_vm(
+            vm1, dhcp_options_isolated_network)
+
+        dhcp_options_vpc_network = self.get_extra_dhcp_options_starting_with(
+            28, None)
+        self.when_i_add_an_extra_nic_to_a_vm(
+            vm1, self.vpc_network, dhcp_options_vpc_network)
+
+        self.when_i_stop_and_start_a_vm(vm1)
+
+        dhcp_options_to_verify_on_default_nic = \
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                16, domain_name, False)
+
+        dhcp_options_to_verify_on_second_nic = \
+            self.get_extra_dhcp_options_to_verify_starting_with(
+                28, domain_name, remove_dns_options=True)
+
+        # dhcp options get applied after start stop vm
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm1, network, dhcp_options_to_verify_on_default_nic,
+            verify_on_vm=True)
+
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm1, network, dhcp_options_to_verify_on_second_nic,
+            network_with_options=self.vpc_network,
+            verify_on_vm=True,
+            default_network=network,
+            vpc=None)
+
+        network.restart(self.api_client, True)
+        self.vpc_network.restart(self.api_client, True)
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm1, network, dhcp_options_to_verify_on_default_nic,
+            verify_on_vm=True)
+
+        self.then_verify_dhcp_options_on_vsd_and_vm(
+            vm1, network, dhcp_options_to_verify_on_second_nic,
+            network_with_options=self.vpc_network,
+            verify_on_vm=True,
+            default_network=network,
+            vpc=None)
+
+        self.delete_VM(vm1)
diff --git a/tools/marvin/marvin/config/test_data.py b/tools/marvin/marvin/config/test_data.py
index 13cd063bfa1..7d8b6266766 100644
--- a/tools/marvin/marvin/config/test_data.py
+++ b/tools/marvin/marvin/config/test_data.py
@@ -1952,6 +1952,42 @@
                 "Dns": "VpcVirtualRouter"
             }
         },
+        # Services supported by the Nuage VSP plugin for VPC without userdata
+        "vpc_network_offering_nuage_dhcp": {
+            "name": 'nuage_vpc_marvin',
+            "displaytext": 'nuage_vpc_marvin',
+            "guestiptype": 'Isolated',
+            "supportedservices": 'Dhcp,StaticNat,SourceNat,NetworkACL,Connectivity,Dns',
+            "traffictype": 'GUEST',
+            "availability": 'Optional',
+            "useVpc": 'on',
+            "ispersistent": 'True',
+            "serviceProviderList": {
+                "Dhcp": "NuageVsp",
+                "StaticNat": "NuageVsp",
+                "SourceNat": "NuageVsp",
+                "NetworkACL": "NuageVsp",
+                "Connectivity": "NuageVsp",
+                "Dns": "VpcVirtualRouter",
+            },
+            "serviceCapabilityList": {
+                "SourceNat": {"SupportedSourceNatTypes": "perzone"}
+            }
+        },
+        # Services supported by the Nuage VSP plugin for VPCs
+        "vpc_offering_nuage_dhcp": {
+            "name": 'Nuage VSP VPC offering',
+            "displaytext": 'Nuage VSP VPC offering',
+            "supportedservices": 'Dhcp,StaticNat,SourceNat,NetworkACL,Connectivity,Dns',
+            "serviceProviderList": {
+                "Dhcp": "NuageVsp",
+                "StaticNat": "NuageVsp",
+                "SourceNat": "NuageVsp",
+                "NetworkACL": "NuageVsp",
+                "Connectivity": "NuageVsp",
+                "Dns": "VpcVirtualRouter",
+            }
+        },
         "shared_nuage_network_offering": {
             "name": 'nuage_marvin',
             "displaytext": 'nuage_marvin',
@@ -2048,3 +2084,4 @@
         }
     }
 }
+
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 4bac4e0cd46..340419a65d2 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -539,6 +539,9 @@ def create(cls, apiclient, services, templateid=None, accountid=None,
         if "userdata" in services:
             cmd.userdata = base64.urlsafe_b64encode(services["userdata"])
 
+        if "dhcpoptionsnetworklist" in services:
+            cmd.dhcpoptionsnetworklist = services["dhcpoptionsnetworklist"]
+
         cmd.details = [{}]
 
         if customcpunumber:
@@ -780,7 +783,7 @@ def detach_volume(self, apiclient, volume):
         cmd.id = volume.id
         return apiclient.detachVolume(cmd)
 
-    def add_nic(self, apiclient, networkId, ipaddress=None, macaddress=None):
+    def add_nic(self, apiclient, networkId, ipaddress=None, macaddress=None, dhcpoptions=None):
         """Add a NIC to a VM"""
         cmd = addNicToVirtualMachine.addNicToVirtualMachineCmd()
         cmd.virtualmachineid = self.id
@@ -788,6 +791,8 @@ def add_nic(self, apiclient, networkId, ipaddress=None, macaddress=None):
 
         if ipaddress:
             cmd.ipaddress = ipaddress
+        if dhcpoptions:
+            cmd.dhcpoptions = dhcpoptions
 
         if macaddress:
             cmd.macaddress = macaddress
diff --git a/utils/src/main/java/com/cloud/utils/net/Dhcp.java b/utils/src/main/java/com/cloud/utils/net/Dhcp.java
new file mode 100644
index 00000000000..33a7db67fa1
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/net/Dhcp.java
@@ -0,0 +1,125 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.net;
+
+import java.util.Arrays;
+
+public class Dhcp {
+    public enum DhcpOptionCode {
+        NETMASK(1, "netmask"),
+        TIME_OFFSET(2, "time-offset"),
+        ROUTER(3, "router"),
+        TIME_SERVER(4, "time-server"),
+        DNS_SERVER(6, "dns-server"),
+        LOG_SERVER(7, "log-server"),
+        LPR_SERVER(9, "lpr-server"),
+        HOSTNAME(12, "hostname"),
+        BOOT_FILE_SIZE(13, "boot-file-size"),
+        DOMAIN_NAME(15, "domain-name"),
+        SWAP_SERVER(16, "swap-server"),
+        ROOT_PATH(17, "root-path"),
+        EXTENSION_PATH(18, "extension-path"),
+        IP_FORWARD_ENABLE(19, "ip-forward-enable"),
+        NON_LOCAL_SOURCE_ROUTING(20, "non-local-source-routing"),
+        POLICY_FILTER(21, "policy-filter"),
+        MAX_DATAGRAM_REASSEMBLY(22, "max-datagram-reassembly"),
+        DEFAULT_TTL(23, "default-ttl"),
+        MTU(26, "mtu"),
+        ALL_SUBNETS_LOCAL(27, "all-subnets-local"),
+        BROADCAST(28, "broadcast"),
+        ROUTER_DISCOVERY(31, "router-discovery"),
+        ROUTER_SOLICITATION(32, "router-solicitation"),
+        STATIC_ROUTE(33, "static-route"),
+        TRAILER_ENCAPSULATION(34, "trailer-encapsulation"),
+        ARP_TIMEOUT(35, "arp-timeout"),
+        ETHERNET_ENCAP(36, "ethernet-encap"),
+        TCP_TTL(37, "tcp-ttl"),
+        TCP_KEEPALIVE(38, "tcp-keepalive"),
+        NIS_DOMAIN(40, "nis-domain"),
+        NIS_SERVER(41, "nis-server"),
+        NTP_SERVER(42, "ntp-server"),
+        VENDOR_ENCAP(43, "vendor-encap"),
+        NETBIOS_NS(44, "netbios-ns"),
+        NETBIOS_DD(45, "netbios-dd"),
+        NETBIOS_NODETYPE(46, "netbios-nodetype"),
+        NETBIOS_SCOPE(47, "netbios-scope"),
+        X_WINDOWS_FS(48, "x-windows-fs"),
+        X_WINDOWS_DM(49, "x-windows-dm"),
+        REQUESTED_ADDRESS(50, "requested-address"),
+        LEASE_TIME(51, "lease-time"),
+        OPTION_OVERLOAD(52, "option-overload"),
+        MESSAGE_TYPE(53, "message-type"),
+        SERVER_IDENTIFIER(54, "server-identifier"),
+        PARAMETER_REQUEST(55, "parameter-request"),
+        MESSAGE(56, "message"),
+        MAX_MESSAGE_SIZE(57, "max-message-size"),
+        T1(58, "T1"),
+        T2(59, "T2"),
+        VENDOR_CLASS(60, "vendor-class"),
+        CLIENT_ID(61, "client-id"),
+        NISPLUS_DOMAIN(64, "nis+-domain"),
+        NISPLUS_SERVER(65, "nis+-server"),
+        TFTP_SERVER(66, "tftp-server"),
+        BOOTFILE_NAME(67, "bootfile-name"),
+        MOBILE_IP_HOME(68, "mobile-ip-home"),
+        SMTP_SERVER(69, "smtp-server"),
+        POP3_SERVER(70, "pop3-server"),
+        NNTP_SERVER(71, "nntp-server"),
+        IRC_SERVER(74, "irc-server"),
+        USER_CLASS(77, "user-class"),
+        CLIENT_ARCH(93, "client-arch"),
+        CLIENT_INTERFACE_ID(94, "client-interface-id"),
+        CLIENT_MACHINE_ID(97, "client-machine-id"),
+        URL(114, "url"),
+        DOMAIN_SEARCH(119, "domain-search"),
+        SIP_SERVER(120, "sip-server"),
+        CLASSLESS_STATIC_ROUTE(121, "classless-static-route"),
+        VENDOR_ID_ENCAP(125, "vendor-id-encap"),
+        SERVER_IP_ADDRESS(255, "server-ip-address");
+
+        private int code;
+        private String name;
+
+        DhcpOptionCode(int code, String name){
+            this.code = code;
+            this.name = name;
+        }
+
+        public int getCode() {
+            return code;
+        }
+
+        public String getName() { return name; }
+
+        public static DhcpOptionCode valueOfInt(int code) {
+            return Arrays.stream(DhcpOptionCode.values())
+                    .filter(option -> option.getCode() == code)
+                    .findFirst()
+                    .orElseThrow(() -> new IllegalArgumentException("Dhcp option code " + code + " not supported."));
+        }
+
+        public static DhcpOptionCode valueOfString(String name) {
+            return Arrays.stream(DhcpOptionCode.values())
+                    .filter(option -> option.getName().equals(name))
+                    .findFirst()
+                    .orElseThrow(() -> new IllegalArgumentException("Dhcp option " + name + " not supported."));
+        }
+    }
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


> extra DHCP option support
> -------------------------
>
>                 Key: CLOUDSTACK-9776
>                 URL: https://issues.apache.org/jira/browse/CLOUDSTACK-9776
>             Project: CloudStack
>          Issue Type: New Feature
>      Security Level: Public(Anyone can view this level - this is the default.) 
>          Components: API, Network Devices
>    Affects Versions: Future
>            Reporter: Sigert Goeminne
>            Assignee: Sigert Goeminne
>
> Cloudstack currently does not allow users to set extra DHCP options. This feature will add support for adding extra (user defined) DHCP options on every nic of a VM in Cloudstack.
>  
> CloudStack currently supports the following DHCP options:
> 1: netmask
> 3: router
> 6: dns-server
> 15: domain name
> [Design Document|https://cwiki.apache.org/confluence/display/CLOUDSTACK/CloudStack+extra+DHCP+option+support]
> [PR 2049|https://github.com/apache/cloudstack/pull/2049]



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)

Mime
View raw message