2222import static com .cloud .network .NetworkModel .CONFIGDATA_FILE ;
2323import static com .cloud .network .NetworkModel .PASSWORD_FILE ;
2424import static com .cloud .network .NetworkModel .USERDATA_FILE ;
25+ import static com .cloud .network .NetworkService .DEFAULT_MTU ;
26+ import static org .apache .cloudstack .storage .configdrive .ConfigDriveUtils .mergeJsonArraysAndUpdateObject ;
2527
2628import java .io .File ;
2729import java .io .IOException ;
3335import java .util .Map ;
3436import java .util .Set ;
3537
38+ import com .cloud .network .Network ;
39+ import com .cloud .vm .NicProfile ;
40+ import com .googlecode .ipv6 .IPv6Network ;
3641import org .apache .commons .codec .binary .Base64 ;
3742import org .apache .commons .collections .MapUtils ;
3843import org .apache .commons .io .FileUtils ;
@@ -81,7 +86,7 @@ static void writeFile(File folder, String file, String content) {
8186
8287 /**
8388 * Read the content of a {@link File} and convert it to a String in base 64.
84- * We expect the content of the file to be encoded using {@link StandardCharsets#US_ASC }
89+ * We expect the content of the file to be encoded using {@link StandardCharsets#US_ASCII }
8590 */
8691 public static String fileToBase64String (File isoFile ) throws IOException {
8792 byte [] encoded = Base64 .encodeBase64 (FileUtils .readFileToByteArray (isoFile ));
@@ -108,9 +113,9 @@ public static File base64StringToFile(String encodedIsoData, String folder, Stri
108113 * 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.
109114 * If vmData is null, we throw a {@link CloudRuntimeException}. Moreover, {@link IOException} are captured and re-thrown as {@link CloudRuntimeException}.
110115 */
111- public static String buildConfigDrive (List <String []> vmData , String isoFileName , String driveLabel , Map <String , String > customUserdataParams ) {
112- if (vmData == null ) {
113- throw new CloudRuntimeException ("No VM metadata provided" );
116+ public static String buildConfigDrive (List <NicProfile > nics , List < String []> vmData , String isoFileName , String driveLabel , Map <String , String > customUserdataParams , Map < Long , List < Network . Service >> supportedServices ) {
117+ if (vmData == null && nics == null ) {
118+ throw new CloudRuntimeException ("No VM metadata and nic profile provided" );
114119 }
115120
116121 Path tempDir = null ;
@@ -121,10 +126,19 @@ public static String buildConfigDrive(List<String[]> vmData, String isoFileName,
121126
122127 File openStackFolder = new File (tempDirName + ConfigDrive .openStackConfigDriveName );
123128
124- writeVendorAndNetworkEmptyJsonFile (openStackFolder );
125- writeVmMetadata (vmData , tempDirName , openStackFolder , customUserdataParams );
126-
127- linkUserData (tempDirName );
129+ writeVendorEmptyJsonFile (openStackFolder );
130+ writeNetworkData (nics , supportedServices , openStackFolder );
131+ for (NicProfile nic : nics ) {
132+ if (supportedServices .get (nic .getId ()).contains (Network .Service .UserData )) {
133+ if (vmData == null ) {
134+ throw new CloudRuntimeException ("No VM metadata provided" );
135+ }
136+ writeVmMetadata (vmData , tempDirName , openStackFolder , customUserdataParams );
137+
138+ linkUserData (tempDirName );
139+ break ;
140+ }
141+ }
128142
129143 return generateAndRetrieveIsoAsBase64Iso (isoFileName , driveLabel , tempDirName );
130144 } catch (IOException e ) {
@@ -212,18 +226,36 @@ static void writeVmMetadata(List<String[]> vmData, String tempDirName, File open
212226 }
213227
214228 /**
215- * Writes the following empty JSON files:
216- * <ul>
217- * <li> vendor_data.json
218- * <li> network_data.json
219- * </ul>
229+ * First we generate a JSON object using {@link #getNetworkDataJsonObjectForNic(NicProfile, List)}, then we write it to a file called "network_data.json".
230+ */
231+ static void writeNetworkData (List <NicProfile > nics , Map <Long , List <Network .Service >> supportedServices , File openStackFolder ) {
232+ JsonObject finalNetworkData = new JsonObject ();
233+ if (needForGeneratingNetworkData (supportedServices )) {
234+ for (NicProfile nic : nics ) {
235+ List <Network .Service > supportedService = supportedServices .get (nic .getId ());
236+ JsonObject networkData = getNetworkDataJsonObjectForNic (nic , supportedService );
237+
238+ mergeJsonArraysAndUpdateObject (finalNetworkData , networkData , "links" , "id" , "type" );
239+ mergeJsonArraysAndUpdateObject (finalNetworkData , networkData , "networks" , "id" , "type" );
240+ mergeJsonArraysAndUpdateObject (finalNetworkData , networkData , "services" , "address" , "type" );
241+ }
242+ }
243+
244+ writeFile (openStackFolder , "network_data.json" , finalNetworkData .toString ());
245+ }
246+
247+ static boolean needForGeneratingNetworkData (Map <Long , List <Network .Service >> supportedServices ) {
248+ return supportedServices .values ().stream ().anyMatch (services -> services .contains (Network .Service .Dhcp ) || services .contains (Network .Service .Dns ));
249+ }
250+
251+ /**
252+ * Writes an empty JSON file named vendor_data.json in openStackFolder
220253 *
221- * If the folder does not exist and we cannot create it, we throw a {@link CloudRuntimeException}.
254+ * If the folder does not exist, and we cannot create it, we throw a {@link CloudRuntimeException}.
222255 */
223- static void writeVendorAndNetworkEmptyJsonFile (File openStackFolder ) {
256+ static void writeVendorEmptyJsonFile (File openStackFolder ) {
224257 if (openStackFolder .exists () || openStackFolder .mkdirs ()) {
225258 writeFile (openStackFolder , "vendor_data.json" , "{}" );
226- writeFile (openStackFolder , "network_data.json" , "{}" );
227259 } else {
228260 throw new CloudRuntimeException ("Failed to create folder " + openStackFolder );
229261 }
@@ -250,6 +282,120 @@ static JsonObject createJsonObjectWithVmData(List<String[]> vmData, String tempD
250282 return metaData ;
251283 }
252284
285+ /**
286+ * Creates the {@link JsonObject} using @param nic's metadata. We expect the JSONObject to have the following entries:
287+ * <ul>
288+ * <li> links </li>
289+ * <li> networks </li>
290+ * <li> services </li>
291+ * </ul>
292+ */
293+ static JsonObject getNetworkDataJsonObjectForNic (NicProfile nic , List <Network .Service > supportedServices ) {
294+ JsonObject networkData = new JsonObject ();
295+
296+ JsonArray links = getLinksJsonArrayForNic (nic );
297+ JsonArray networks = getNetworksJsonArrayForNic (nic );
298+ if (links .size () > 0 ) {
299+ networkData .add ("links" , links );
300+ }
301+ if (networks .size () > 0 ) {
302+ networkData .add ("networks" , networks );
303+ }
304+
305+ JsonArray services = getServicesJsonArrayForNic (nic );
306+ if (services .size () > 0 ) {
307+ networkData .add ("services" , services );
308+ }
309+
310+ return networkData ;
311+ }
312+
313+ static JsonArray getLinksJsonArrayForNic (NicProfile nic ) {
314+ JsonArray links = new JsonArray ();
315+ if (StringUtils .isNotBlank (nic .getMacAddress ())) {
316+ JsonObject link = new JsonObject ();
317+ link .addProperty ("ethernet_mac_address" , nic .getMacAddress ());
318+ link .addProperty ("id" , String .format ("eth%d" , nic .getDeviceId ()));
319+ link .addProperty ("mtu" , nic .getMtu () != null ? nic .getMtu () : DEFAULT_MTU );
320+ link .addProperty ("type" , "phy" );
321+ links .add (link );
322+ }
323+ return links ;
324+ }
325+
326+ static JsonArray getNetworksJsonArrayForNic (NicProfile nic ) {
327+ JsonArray networks = new JsonArray ();
328+ if (StringUtils .isNotBlank (nic .getIPv4Address ())) {
329+ JsonObject ipv4Network = new JsonObject ();
330+ ipv4Network .addProperty ("id" , String .format ("eth%d" , nic .getDeviceId ()));
331+ ipv4Network .addProperty ("ip_address" , nic .getIPv4Address ());
332+ ipv4Network .addProperty ("link" , String .format ("eth%d" , nic .getDeviceId ()));
333+ ipv4Network .addProperty ("netmask" , nic .getIPv4Netmask ());
334+ ipv4Network .addProperty ("network_id" , nic .getUuid ());
335+ ipv4Network .addProperty ("type" , "ipv4" );
336+
337+ JsonArray ipv4RouteArray = new JsonArray ();
338+ JsonObject ipv4Route = new JsonObject ();
339+ ipv4Route .addProperty ("gateway" , nic .getIPv4Gateway ());
340+ ipv4Route .addProperty ("netmask" , "0.0.0.0" );
341+ ipv4Route .addProperty ("network" , "0.0.0.0" );
342+ ipv4RouteArray .add (ipv4Route );
343+
344+ ipv4Network .add ("routes" , ipv4RouteArray );
345+
346+ networks .add (ipv4Network );
347+ }
348+
349+ if (StringUtils .isNotBlank (nic .getIPv6Address ())) {
350+ JsonObject ipv6Network = new JsonObject ();
351+ ipv6Network .addProperty ("id" , String .format ("eth%d" , nic .getDeviceId ()));
352+ ipv6Network .addProperty ("ip_address" , nic .getIPv6Address ());
353+ ipv6Network .addProperty ("link" , String .format ("eth%d" , nic .getDeviceId ()));
354+ ipv6Network .addProperty ("netmask" , IPv6Network .fromString (nic .getIPv6Cidr ()).getNetmask ().toString ());
355+ ipv6Network .addProperty ("network_id" , nic .getUuid ());
356+ ipv6Network .addProperty ("type" , "ipv6" );
357+
358+ JsonArray ipv6RouteArray = new JsonArray ();
359+ JsonObject ipv6Route = new JsonObject ();
360+ ipv6Route .addProperty ("gateway" , nic .getIPv6Gateway ());
361+ ipv6Route .addProperty ("netmask" , "0" );
362+ ipv6Route .addProperty ("network" , "::" );
363+ ipv6RouteArray .add (ipv6Route );
364+
365+ ipv6Network .add ("routes" , ipv6RouteArray );
366+
367+ networks .add (ipv6Network );
368+ }
369+ return networks ;
370+ }
371+
372+ static JsonArray getServicesJsonArrayForNic (NicProfile nic ) {
373+ JsonArray services = new JsonArray ();
374+ if (StringUtils .isNotBlank (nic .getIPv4Dns1 ())) {
375+ services .add (getDnsServiceObject (nic .getIPv4Dns1 ()));
376+ }
377+
378+ if (StringUtils .isNotBlank (nic .getIPv4Dns2 ())) {
379+ services .add (getDnsServiceObject (nic .getIPv4Dns2 ()));
380+ }
381+
382+ if (StringUtils .isNotBlank (nic .getIPv6Dns1 ())) {
383+ services .add (getDnsServiceObject (nic .getIPv6Dns1 ()));
384+ }
385+
386+ if (StringUtils .isNotBlank (nic .getIPv6Dns2 ())) {
387+ services .add (getDnsServiceObject (nic .getIPv6Dns2 ()));
388+ }
389+ return services ;
390+ }
391+
392+ private static JsonObject getDnsServiceObject (String dnsAddress ) {
393+ JsonObject dnsService = new JsonObject ();
394+ dnsService .addProperty ("address" , dnsAddress );
395+ dnsService .addProperty ("type" , "dns" );
396+ return dnsService ;
397+ }
398+
253399 static void createFileInTempDirAnAppendOpenStackMetadataToJsonObject (String tempDirName , JsonObject metaData , String dataType , String fileName , String content , Map <String , String > customUserdataParams ) {
254400 if (StringUtils .isBlank (dataType )) {
255401 return ;
0 commit comments