This is an automated email from the ASF dual-hosted git repository.

vishesh pushed a commit to branch configdrive-network-data
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 227be3dabf6aa5b7f35a62570cf4c1352f1429d2
Author: Vishesh <vishes...@gmail.com>
AuthorDate: Wed Jul 3 13:10:17 2024 +0530

    Add support for network data in Config Drive
---
 .../engine/orchestration/NetworkOrchestrator.java  |  8 ++
 .../storage/configdrive/ConfigDriveBuilder.java    | 94 +++++++++++++++++++++-
 .../network/element/ConfigDriveNetworkElement.java | 72 ++++++++++++++---
 3 files changed, 162 insertions(+), 12 deletions(-)

diff --git 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index ea34f62ecd5..fccb2797275 100644
--- 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -1798,6 +1798,7 @@ public class NetworkOrchestrator extends ManagerBase 
implements NetworkOrchestra
                                      final ReservationContext context) throws 
InsufficientCapacityException, ConcurrentOperationException, 
ResourceUnavailableException {
         element.prepare(network, profile, vmProfile, dest, context);
         if (vmProfile.getType() == Type.User && element.getProvider() != null) 
{
+            boolean buildConfigDrive = false;
             if (_networkModel.areServicesSupportedInNetwork(network.getId(), 
Service.Dhcp)
                     && 
_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, 
element.getProvider()) && element instanceof DhcpServiceProvider) {
                 final DhcpServiceProvider sp = (DhcpServiceProvider) element;
@@ -1809,6 +1810,7 @@ public class NetworkOrchestrator extends ManagerBase 
implements NetworkOrchestra
                 if (!sp.addDhcpEntry(network, profile, vmProfile, dest, 
context)) {
                     return false;
                 }
+                buildConfigDrive = true;
             }
             if (_networkModel.areServicesSupportedInNetwork(network.getId(), 
Service.Dns)
                     && 
_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns, 
element.getProvider()) && element instanceof DnsServiceProvider) {
@@ -1821,6 +1823,7 @@ public class NetworkOrchestrator extends ManagerBase 
implements NetworkOrchestra
                 if (!sp.addDnsEntry(network, profile, vmProfile, dest, 
context)) {
                     return false;
                 }
+                buildConfigDrive = true;
             }
             if (_networkModel.areServicesSupportedInNetwork(network.getId(), 
Service.UserData)
                     && 
_networkModel.isProviderSupportServiceInNetwork(network.getId(), 
Service.UserData, element.getProvider()) && element instanceof 
UserDataServiceProvider) {
@@ -1828,6 +1831,11 @@ public class NetworkOrchestrator extends ManagerBase 
implements NetworkOrchestra
                 if (!sp.addPasswordAndUserdata(network, profile, vmProfile, 
dest, context)) {
                     return false;
                 }
+                buildConfigDrive = true;
+            }
+            if (buildConfigDrive && element instanceof 
ConfigDriveNetworkElement) {
+                final ConfigDriveNetworkElement sp = 
(ConfigDriveNetworkElement) element;
+                return sp.createConfigDriveIso(profile, vmProfile, dest, null);
             }
         }
         return true;
diff --git 
a/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java
 
b/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java
index e1d51120efa..14969c18078 100644
--- 
a/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java
+++ 
b/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java
@@ -33,6 +33,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import com.cloud.vm.NicProfile;
+import com.googlecode.ipv6.IPv6Network;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.io.FileUtils;
@@ -108,9 +110,9 @@ public class ConfigDriveBuilder {
      *  This method will build the metadata files required by OpenStack 
driver. Then, an ISO is going to be generated and returned as a String in base 
64.
      *  If vmData is null, we throw a {@link CloudRuntimeException}. Moreover, 
{@link IOException} are captured and re-thrown as {@link CloudRuntimeException}.
      */
-    public static String buildConfigDrive(List<String[]> vmData, String 
isoFileName, String driveLabel, Map<String, String> customUserdataParams) {
-        if (vmData == null) {
-            throw new CloudRuntimeException("No VM metadata provided");
+    public static String buildConfigDrive(NicProfile nic, List<String[]> 
vmData, String isoFileName, String driveLabel, Map<String, String> 
customUserdataParams) {
+        if (vmData == null && nic == null) {
+            throw new CloudRuntimeException("No VM metadata and nic profile 
provided");
         }
 
         Path tempDir = null;
@@ -122,6 +124,7 @@ public class ConfigDriveBuilder {
             File openStackFolder = new File(tempDirName + 
ConfigDrive.openStackConfigDriveName);
 
             writeVendorAndNetworkEmptyJsonFile(openStackFolder);
+            writeNetworkData(nic, tempDirName, openStackFolder);
             writeVmMetadata(vmData, tempDirName, openStackFolder, 
customUserdataParams);
 
             linkUserData(tempDirName);
@@ -211,6 +214,14 @@ public class ConfigDriveBuilder {
         writeFile(openStackFolder, "meta_data.json", metaData.toString());
     }
 
+    /**
+     * First we generate a JSON object using {@link 
#createJsonObjectWithNic(NicProfile)}, then we write it to a file called 
"network_data.json".
+     */
+    static void writeNetworkData(NicProfile nic, String tempDirName, File 
openStackFolder) {
+        JsonObject networkData = createJsonObjectWithNic(nic);
+        writeFile(openStackFolder, "network_data.json", 
networkData.toString());
+    }
+
     /**
      *  Writes the following empty JSON files:
      *  <ul>
@@ -250,6 +261,83 @@ public class ConfigDriveBuilder {
         return metaData;
     }
 
+    /**
+     * Creates the {@link JsonObject} with NIC's metadata. We expect the 
JSONObject to have the following entries:
+     */
+    static JsonObject createJsonObjectWithNic(NicProfile nic) {
+        // TODO: Check if we actually need to read the existing file and 
upsert the data to support multiple networks
+        JsonObject networkData = new JsonObject();
+        JsonArray links = new JsonArray();
+        JsonArray networks = new JsonArray();
+        JsonArray services = new JsonArray();
+
+        if (StringUtils.isNotBlank(nic.getMacAddress())) {
+            JsonObject link = new JsonObject();
+            link.addProperty("ethernet_mac_address", nic.getMacAddress());
+            link.addProperty("id", "eth0");
+            link.addProperty("mtu", 1500);
+            link.addProperty("type", "phy");
+            links.add(link);
+        }
+
+        if (StringUtils.isNotBlank(nic.getIPv4Address())) {
+            JsonObject ipv4Network = new JsonObject();
+            ipv4Network.addProperty("id", nic.getIPv4Address());
+            ipv4Network.addProperty("ip_address", nic.getIPv4Address());
+            ipv4Network.addProperty("link", "eth0");
+            ipv4Network.addProperty("netmask", nic.getIPv4Netmask());
+            ipv4Network.addProperty("network_id", nic.getNetworkId());
+            ipv4Network.addProperty("type", "ipv4");
+
+            JsonObject ipv4Route = new JsonObject();
+            ipv4Route.addProperty("gateway", nic.getIPv4Gateway());
+            ipv4Route.addProperty("netmask", "0.0.0.0");
+            ipv4Route.addProperty("network", "0.0.0.0");
+            networks.add(ipv4Network);
+        }
+
+        if (StringUtils.isNotBlank(nic.getIPv6Address())) {
+            JsonObject ipv6Network = new JsonObject();
+            ipv6Network.addProperty("id", nic.getIPv6Address());
+            ipv6Network.addProperty("ip_address", nic.getIPv6Address());
+            ipv6Network.addProperty("link", "eth0");
+            ipv6Network.addProperty("netmask", 
IPv6Network.fromString(nic.getIPv6Cidr()).getNetmask().toString());
+            ipv6Network.addProperty("network_id", nic.getNetworkId());
+            ipv6Network.addProperty("type", "ipv6");
+
+            JsonObject ipv6Route = new JsonObject();
+            ipv6Route.addProperty("gateway", nic.getIPv6Gateway());
+            ipv6Route.addProperty("netmask", "0");
+            ipv6Route.addProperty("network", "::");
+            networks.add(ipv6Network);
+        }
+
+        if (StringUtils.isNotBlank(nic.getIPv4Dns1())) {
+            services.add(getDnsServiceObject(nic.getIPv4Dns1()));
+        }
+
+        if (StringUtils.isNotBlank(nic.getIPv4Dns2())) {
+            services.add(getDnsServiceObject(nic.getIPv4Dns2()));
+        }
+
+        if (StringUtils.isNotBlank(nic.getIPv6Dns1())) {
+            services.add(getDnsServiceObject(nic.getIPv6Dns1()));
+        }
+
+        if (StringUtils.isNotBlank(nic.getIPv6Dns2())) {
+            services.add(getDnsServiceObject(nic.getIPv6Dns2()));
+        }
+
+        return networkData;
+    }
+
+    private static JsonObject getDnsServiceObject(String dnsAddress) {
+        JsonObject dnsService = new JsonObject();
+        dnsService.addProperty("address", dnsAddress);
+        dnsService.addProperty("type", "dns");
+        return dnsService;
+    }
+
     static void 
createFileInTempDirAnAppendOpenStackMetadataToJsonObject(String tempDirName, 
JsonObject metaData, String dataType, String fileName, String content, 
Map<String, String> customUserdataParams) {
         if (StringUtils.isBlank(dataType)) {
             return;
diff --git 
a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java 
b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
index a9fa3e95275..82d51d04160 100644
--- 
a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
+++ 
b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
@@ -90,7 +90,8 @@ import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
 
-public class ConfigDriveNetworkElement extends AdapterBase implements 
NetworkElement, UserDataServiceProvider,
+public class ConfigDriveNetworkElement extends AdapterBase implements 
NetworkElement,
+        UserDataServiceProvider, DhcpServiceProvider, DnsServiceProvider,
         StateListener<VirtualMachine.State, VirtualMachine.Event, 
VirtualMachine>, NetworkMigrationResponder {
 
     private static final Map<Service, Map<Capability, String>> capabilities = 
setCapabilities();
@@ -197,6 +198,8 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
     private static Map<Service, Map<Capability, String>> setCapabilities() {
         Map<Service, Map<Capability, String>> capabilities = new HashMap<>();
         capabilities.put(Service.UserData, null);
+        capabilities.put(Service.Dhcp, null);
+        capabilities.put(Service.Dns, null);
         return capabilities;
     }
 
@@ -225,7 +228,7 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
             throws ConcurrentOperationException, 
InsufficientCapacityException, ResourceUnavailableException {
         return (canHandle(network.getTrafficType())
                 && configureConfigDriveData(profile, nic, dest))
-                && createConfigDriveIso(profile, dest, null);
+                && createConfigDriveIso(nic, profile, dest, null);
     }
 
     @Override
@@ -342,10 +345,13 @@ public class ConfigDriveNetworkElement extends 
AdapterBase implements NetworkEle
                     configureConfigDriveData(vm, nic, dest);
 
                     // Create the config drive on dest host cache
-                    createConfigDriveIsoOnHostCache(vm, 
dest.getHost().getId());
+                    createConfigDriveIsoOnHostCache(nic, vm, 
dest.getHost().getId());
                 } else {
                     
vm.setConfigDriveLocation(getConfigDriveLocation(vm.getId()));
-                    addPasswordAndUserdata(network, nic, vm, dest, context);
+                    boolean result = addPasswordAndUserdata(network, nic, vm, 
dest, context);
+                    if (result) {
+                        createConfigDriveIso(nic, vm, dest, null);
+                    }
                 }
             } catch (InsufficientCapacityException | 
ResourceUnavailableException e) {
                 logger.error("Failed to add config disk drive due to: ", e);
@@ -398,7 +404,7 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
                         vm.getUuid(), nic.getMacAddress(), 
userVm.getDetail("SSH.PublicKey"), (String) 
vm.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows, 
VirtualMachineManager.getHypervisorHostname(dest.getHost() != null ? 
dest.getHost().getName() : ""));
                 vm.setVmData(vmData);
                 
vm.setConfigDriveLabel(VirtualMachineManager.VmConfigDriveLabel.value());
-                createConfigDriveIso(vm, dest, diskToUse);
+                createConfigDriveIso(nic, vm, dest, diskToUse);
             }
         }
     }
@@ -528,7 +534,7 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
         return false;
     }
 
-    private boolean createConfigDriveIsoOnHostCache(VirtualMachineProfile 
profile, Long hostId) throws ResourceUnavailableException {
+    private boolean createConfigDriveIsoOnHostCache(NicProfile nic, 
VirtualMachineProfile profile, Long hostId) throws ResourceUnavailableException 
{
         if (hostId == null) {
             throw new ResourceUnavailableException("Config drive iso creation 
failed, dest host not available",
                     ConfigDriveNetworkElement.class, 0L);
@@ -540,7 +546,7 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
 
         final String isoFileName = 
ConfigDrive.configIsoFileName(profile.getInstanceName());
         final String isoPath = 
ConfigDrive.createConfigDrivePath(profile.getInstanceName());
-        final String isoData = 
ConfigDriveBuilder.buildConfigDrive(profile.getVmData(), isoFileName, 
profile.getConfigDriveLabel(), customUserdataParamMap);
+        final String isoData = ConfigDriveBuilder.buildConfigDrive(nic, 
profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), 
customUserdataParamMap);
         final HandleConfigDriveIsoCommand configDriveIsoCommand = new 
HandleConfigDriveIsoCommand(isoPath, isoData, null, false, true, true);
 
         final HandleConfigDriveIsoAnswer answer = (HandleConfigDriveIsoAnswer) 
agentManager.easySend(hostId, configDriveIsoCommand);
@@ -590,7 +596,7 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
         return true;
     }
 
-    private boolean createConfigDriveIso(VirtualMachineProfile profile, 
DeployDestination dest, DiskTO disk) throws ResourceUnavailableException {
+    public boolean createConfigDriveIso(NicProfile nic, VirtualMachineProfile 
profile, DeployDestination dest, DiskTO disk) throws 
ResourceUnavailableException {
         DataStore dataStore = getDatastoreForConfigDriveIso(disk, profile, 
dest);
 
         final Long agentId = findAgentId(profile, dest, dataStore);
@@ -605,7 +611,7 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
 
         final String isoFileName = 
ConfigDrive.configIsoFileName(profile.getInstanceName());
         final String isoPath = 
ConfigDrive.createConfigDrivePath(profile.getInstanceName());
-        final String isoData = 
ConfigDriveBuilder.buildConfigDrive(profile.getVmData(), isoFileName, 
profile.getConfigDriveLabel(), customUserdataParamMap);
+        final String isoData = ConfigDriveBuilder.buildConfigDrive(nic, 
profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), 
customUserdataParamMap);
         boolean useHostCacheOnUnsupportedPool = 
VirtualMachineManager.VmConfigDriveUseHostCacheOnUnsupportedPool.valueIn(dest.getDataCenter().getId());
         boolean preferHostCache = 
VirtualMachineManager.VmConfigDriveForceHostCacheUse.valueIn(dest.getDataCenter().getId());
         final HandleConfigDriveIsoCommand configDriveIsoCommand = new 
HandleConfigDriveIsoCommand(isoPath, isoData, dataStore.getTO(), 
useHostCacheOnUnsupportedPool, preferHostCache, true);
@@ -758,4 +764,52 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
         return true;
     }
 
+    @Override
+    public boolean addDhcpEntry(Network network, NicProfile nic, 
VirtualMachineProfile vm, DeployDestination dest,
+            ReservationContext context) throws ConcurrentOperationException, 
InsufficientCapacityException, ResourceUnavailableException {
+        // Update nic profile with required information.
+        // Add network checks
+        return true;
+    }
+
+    @Override
+    public boolean configDhcpSupportForSubnet(Network network, NicProfile nic, 
VirtualMachineProfile vm,
+            DeployDestination dest,
+            ReservationContext context) throws ConcurrentOperationException, 
InsufficientCapacityException, ResourceUnavailableException {
+        return false;
+    }
+
+    @Override
+    public boolean removeDhcpSupportForSubnet(Network network) throws 
ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean setExtraDhcpOptions(Network network, long nicId, 
Map<Integer, String> dhcpOptions) {
+        return false;
+    }
+
+    @Override
+    public boolean removeDhcpEntry(Network network, NicProfile nic,
+            VirtualMachineProfile vmProfile) throws 
ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean addDnsEntry(Network network, NicProfile nic, 
VirtualMachineProfile vm, DeployDestination dest,
+            ReservationContext context) throws ConcurrentOperationException, 
InsufficientCapacityException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean configDnsSupportForSubnet(Network network, NicProfile nic, 
VirtualMachineProfile vm,
+            DeployDestination dest,
+            ReservationContext context) throws ConcurrentOperationException, 
InsufficientCapacityException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean removeDnsSupportForSubnet(Network network) throws 
ResourceUnavailableException {
+        return true;
+    }
 }

Reply via email to