mirror of https://github.com/apache/cloudstack.git
Add support for network data in Config Drive (#9329)
This commit is contained in:
parent
c9f1c5790d
commit
bc28665679
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey.Scope;
|
||||
|
|
@ -144,6 +145,8 @@ public interface NetworkOrchestrationService {
|
|||
|
||||
List<NicProfile> getNicProfiles(VirtualMachine vm);
|
||||
|
||||
List<NicProfile> getNicProfiles(Long vmId, Hypervisor.HypervisorType hypervisorType);
|
||||
|
||||
Map<String, String> getSystemVMAccessDetails(VirtualMachine vm);
|
||||
|
||||
Pair<? extends NetworkGuru, ? extends Network> implementNetwork(long networkId, DeployDestination dest, ReservationContext context)
|
||||
|
|
|
|||
|
|
@ -1835,6 +1835,19 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (element instanceof ConfigDriveNetworkElement && ((
|
||||
_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp) &&
|
||||
_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, element.getProvider())
|
||||
) || (
|
||||
_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dns) &&
|
||||
_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns, element.getProvider())
|
||||
) || (
|
||||
_networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData) &&
|
||||
_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.UserData, element.getProvider())
|
||||
))) {
|
||||
final ConfigDriveNetworkElement sp = (ConfigDriveNetworkElement) element;
|
||||
return sp.createConfigDriveIso(profile, vmProfile, dest, null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -4443,18 +4456,18 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<NicProfile> getNicProfiles(final VirtualMachine vm) {
|
||||
final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
|
||||
public List<NicProfile> getNicProfiles(final Long vmId, HypervisorType hypervisorType) {
|
||||
final List<NicVO> nics = _nicDao.listByVmId(vmId);
|
||||
final List<NicProfile> profiles = new ArrayList<NicProfile>();
|
||||
|
||||
if (nics != null) {
|
||||
for (final Nic nic : nics) {
|
||||
final NetworkVO network = _networksDao.findById(nic.getNetworkId());
|
||||
final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId());
|
||||
final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vmId);
|
||||
|
||||
final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
|
||||
final NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate,
|
||||
_networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network));
|
||||
_networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(hypervisorType, network));
|
||||
guru.updateNicProfile(profile, network);
|
||||
profiles.add(profile);
|
||||
}
|
||||
|
|
@ -4462,6 +4475,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
return profiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NicProfile> getNicProfiles(final VirtualMachine vm) {
|
||||
return getNicProfiles(vm.getId(), vm.getHypervisorType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getSystemVMAccessDetails(final VirtualMachine vm) {
|
||||
final Map<String, String> accessDetails = new HashMap<>();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import static com.cloud.network.NetworkModel.CONFIGDATA_DIR;
|
|||
import static com.cloud.network.NetworkModel.CONFIGDATA_FILE;
|
||||
import static com.cloud.network.NetworkModel.PASSWORD_FILE;
|
||||
import static com.cloud.network.NetworkModel.USERDATA_FILE;
|
||||
import static com.cloud.network.NetworkService.DEFAULT_MTU;
|
||||
import static org.apache.cloudstack.storage.configdrive.ConfigDriveUtils.mergeJsonArraysAndUpdateObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
|
@ -33,6 +35,9 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.cloud.network.Network;
|
||||
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;
|
||||
|
|
@ -81,7 +86,7 @@ public class ConfigDriveBuilder {
|
|||
|
||||
/**
|
||||
* Read the content of a {@link File} and convert it to a String in base 64.
|
||||
* We expect the content of the file to be encoded using {@link StandardCharsets#US_ASC}
|
||||
* We expect the content of the file to be encoded using {@link StandardCharsets#US_ASCII}
|
||||
*/
|
||||
public static String fileToBase64String(File isoFile) throws IOException {
|
||||
byte[] encoded = Base64.encodeBase64(FileUtils.readFileToByteArray(isoFile));
|
||||
|
|
@ -108,9 +113,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(List<NicProfile> nics, List<String[]> vmData, String isoFileName, String driveLabel, Map<String, String> customUserdataParams, Map<Long, List<Network.Service>> supportedServices) {
|
||||
if (vmData == null && nics == null) {
|
||||
throw new CloudRuntimeException("No VM metadata and nic profile provided");
|
||||
}
|
||||
|
||||
Path tempDir = null;
|
||||
|
|
@ -121,10 +126,19 @@ public class ConfigDriveBuilder {
|
|||
|
||||
File openStackFolder = new File(tempDirName + ConfigDrive.openStackConfigDriveName);
|
||||
|
||||
writeVendorAndNetworkEmptyJsonFile(openStackFolder);
|
||||
writeVmMetadata(vmData, tempDirName, openStackFolder, customUserdataParams);
|
||||
writeVendorEmptyJsonFile(openStackFolder);
|
||||
writeNetworkData(nics, supportedServices, openStackFolder);
|
||||
for (NicProfile nic: nics) {
|
||||
if (supportedServices.get(nic.getId()).contains(Network.Service.UserData)) {
|
||||
if (vmData == null) {
|
||||
throw new CloudRuntimeException("No VM metadata provided");
|
||||
}
|
||||
writeVmMetadata(vmData, tempDirName, openStackFolder, customUserdataParams);
|
||||
|
||||
linkUserData(tempDirName);
|
||||
linkUserData(tempDirName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return generateAndRetrieveIsoAsBase64Iso(isoFileName, driveLabel, tempDirName);
|
||||
} catch (IOException e) {
|
||||
|
|
@ -212,18 +226,36 @@ public class ConfigDriveBuilder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Writes the following empty JSON files:
|
||||
* <ul>
|
||||
* <li> vendor_data.json
|
||||
* <li> network_data.json
|
||||
* </ul>
|
||||
*
|
||||
* If the folder does not exist and we cannot create it, we throw a {@link CloudRuntimeException}.
|
||||
* First we generate a JSON object using {@link #getNetworkDataJsonObjectForNic(NicProfile, List)}, then we write it to a file called "network_data.json".
|
||||
*/
|
||||
static void writeVendorAndNetworkEmptyJsonFile(File openStackFolder) {
|
||||
static void writeNetworkData(List<NicProfile> nics, Map<Long, List<Network.Service>> supportedServices, File openStackFolder) {
|
||||
JsonObject finalNetworkData = new JsonObject();
|
||||
if (needForGeneratingNetworkData(supportedServices)) {
|
||||
for (NicProfile nic : nics) {
|
||||
List<Network.Service> supportedService = supportedServices.get(nic.getId());
|
||||
JsonObject networkData = getNetworkDataJsonObjectForNic(nic, supportedService);
|
||||
|
||||
mergeJsonArraysAndUpdateObject(finalNetworkData, networkData, "links", "id", "type");
|
||||
mergeJsonArraysAndUpdateObject(finalNetworkData, networkData, "networks", "id", "type");
|
||||
mergeJsonArraysAndUpdateObject(finalNetworkData, networkData, "services", "address", "type");
|
||||
}
|
||||
}
|
||||
|
||||
writeFile(openStackFolder, "network_data.json", finalNetworkData.toString());
|
||||
}
|
||||
|
||||
static boolean needForGeneratingNetworkData(Map<Long, List<Network.Service>> supportedServices) {
|
||||
return supportedServices.values().stream().anyMatch(services -> services.contains(Network.Service.Dhcp) || services.contains(Network.Service.Dns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an empty JSON file named vendor_data.json in openStackFolder
|
||||
*
|
||||
* If the folder does not exist, and we cannot create it, we throw a {@link CloudRuntimeException}.
|
||||
*/
|
||||
static void writeVendorEmptyJsonFile(File openStackFolder) {
|
||||
if (openStackFolder.exists() || openStackFolder.mkdirs()) {
|
||||
writeFile(openStackFolder, "vendor_data.json", "{}");
|
||||
writeFile(openStackFolder, "network_data.json", "{}");
|
||||
} else {
|
||||
throw new CloudRuntimeException("Failed to create folder " + openStackFolder);
|
||||
}
|
||||
|
|
@ -250,6 +282,120 @@ public class ConfigDriveBuilder {
|
|||
return metaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link JsonObject} using @param nic's metadata. We expect the JSONObject to have the following entries:
|
||||
* <ul>
|
||||
* <li> links </li>
|
||||
* <li> networks </li>
|
||||
* <li> services </li>
|
||||
* </ul>
|
||||
*/
|
||||
static JsonObject getNetworkDataJsonObjectForNic(NicProfile nic, List<Network.Service> supportedServices) {
|
||||
JsonObject networkData = new JsonObject();
|
||||
|
||||
JsonArray links = getLinksJsonArrayForNic(nic);
|
||||
JsonArray networks = getNetworksJsonArrayForNic(nic);
|
||||
if (links.size() > 0) {
|
||||
networkData.add("links", links);
|
||||
}
|
||||
if (networks.size() > 0) {
|
||||
networkData.add("networks", networks);
|
||||
}
|
||||
|
||||
JsonArray services = getServicesJsonArrayForNic(nic);
|
||||
if (services.size() > 0) {
|
||||
networkData.add("services", services);
|
||||
}
|
||||
|
||||
return networkData;
|
||||
}
|
||||
|
||||
static JsonArray getLinksJsonArrayForNic(NicProfile nic) {
|
||||
JsonArray links = new JsonArray();
|
||||
if (StringUtils.isNotBlank(nic.getMacAddress())) {
|
||||
JsonObject link = new JsonObject();
|
||||
link.addProperty("ethernet_mac_address", nic.getMacAddress());
|
||||
link.addProperty("id", String.format("eth%d", nic.getDeviceId()));
|
||||
link.addProperty("mtu", nic.getMtu() != null ? nic.getMtu() : DEFAULT_MTU);
|
||||
link.addProperty("type", "phy");
|
||||
links.add(link);
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
static JsonArray getNetworksJsonArrayForNic(NicProfile nic) {
|
||||
JsonArray networks = new JsonArray();
|
||||
if (StringUtils.isNotBlank(nic.getIPv4Address())) {
|
||||
JsonObject ipv4Network = new JsonObject();
|
||||
ipv4Network.addProperty("id", String.format("eth%d", nic.getDeviceId()));
|
||||
ipv4Network.addProperty("ip_address", nic.getIPv4Address());
|
||||
ipv4Network.addProperty("link", String.format("eth%d", nic.getDeviceId()));
|
||||
ipv4Network.addProperty("netmask", nic.getIPv4Netmask());
|
||||
ipv4Network.addProperty("network_id", nic.getUuid());
|
||||
ipv4Network.addProperty("type", "ipv4");
|
||||
|
||||
JsonArray ipv4RouteArray = new JsonArray();
|
||||
JsonObject ipv4Route = new JsonObject();
|
||||
ipv4Route.addProperty("gateway", nic.getIPv4Gateway());
|
||||
ipv4Route.addProperty("netmask", "0.0.0.0");
|
||||
ipv4Route.addProperty("network", "0.0.0.0");
|
||||
ipv4RouteArray.add(ipv4Route);
|
||||
|
||||
ipv4Network.add("routes", ipv4RouteArray);
|
||||
|
||||
networks.add(ipv4Network);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(nic.getIPv6Address())) {
|
||||
JsonObject ipv6Network = new JsonObject();
|
||||
ipv6Network.addProperty("id", String.format("eth%d", nic.getDeviceId()));
|
||||
ipv6Network.addProperty("ip_address", nic.getIPv6Address());
|
||||
ipv6Network.addProperty("link", String.format("eth%d", nic.getDeviceId()));
|
||||
ipv6Network.addProperty("netmask", IPv6Network.fromString(nic.getIPv6Cidr()).getNetmask().toString());
|
||||
ipv6Network.addProperty("network_id", nic.getUuid());
|
||||
ipv6Network.addProperty("type", "ipv6");
|
||||
|
||||
JsonArray ipv6RouteArray = new JsonArray();
|
||||
JsonObject ipv6Route = new JsonObject();
|
||||
ipv6Route.addProperty("gateway", nic.getIPv6Gateway());
|
||||
ipv6Route.addProperty("netmask", "0");
|
||||
ipv6Route.addProperty("network", "::");
|
||||
ipv6RouteArray.add(ipv6Route);
|
||||
|
||||
ipv6Network.add("routes", ipv6RouteArray);
|
||||
|
||||
networks.add(ipv6Network);
|
||||
}
|
||||
return networks;
|
||||
}
|
||||
|
||||
static JsonArray getServicesJsonArrayForNic(NicProfile nic) {
|
||||
JsonArray services = new JsonArray();
|
||||
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 services;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// 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.storage.configdrive;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ConfigDriveUtils {
|
||||
|
||||
static void mergeJsonArraysAndUpdateObject(JsonObject finalObject, JsonObject newObj, String memberName, String... keys) {
|
||||
JsonArray existingMembers = finalObject.has(memberName) ? finalObject.get(memberName).getAsJsonArray() : new JsonArray();
|
||||
JsonArray newMembers = newObj.has(memberName) ? newObj.get(memberName).getAsJsonArray() : new JsonArray();
|
||||
|
||||
if (existingMembers.size() > 0 || newMembers.size() > 0) {
|
||||
JsonArray finalMembers = new JsonArray();
|
||||
Set<String> idSet = new HashSet<>();
|
||||
for (JsonElement element : existingMembers.getAsJsonArray()) {
|
||||
JsonObject elementObject = element.getAsJsonObject();
|
||||
String key = Arrays.stream(keys).map(elementObject::get).map(JsonElement::getAsString).reduce((a, b) -> a + "-" + b).orElse("");
|
||||
idSet.add(key);
|
||||
finalMembers.add(element);
|
||||
}
|
||||
for (JsonElement element : newMembers.getAsJsonArray()) {
|
||||
JsonObject elementObject = element.getAsJsonObject();
|
||||
String key = Arrays.stream(keys).map(elementObject::get).map(JsonElement::getAsString).reduce((a, b) -> a + "-" + b).orElse("");
|
||||
if (!idSet.contains(key)) {
|
||||
finalMembers.add(element);
|
||||
}
|
||||
}
|
||||
finalObject.add(memberName, finalMembers);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ package org.apache.cloudstack.storage.configdrive;
|
|||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -27,14 +28,21 @@ import java.io.IOException;
|
|||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.vm.NicProfile;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.MockedConstruction;
|
||||
|
|
@ -49,6 +57,13 @@ import com.google.gson.JsonObject;
|
|||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ConfigDriveBuilderTest {
|
||||
|
||||
private static Map<Long, List<Network.Service>> supportedServices;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
supportedServices = Map.of(1L, List.of(Network.Service.UserData, Network.Service.Dhcp, Network.Service.Dns));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeFileTest() {
|
||||
try (MockedStatic<FileUtils> fileUtilsMocked = Mockito.mockStatic(FileUtils.class)) {
|
||||
|
|
@ -112,16 +127,16 @@ public class ConfigDriveBuilderTest {
|
|||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void buildConfigDriveTestNoVmData() {
|
||||
ConfigDriveBuilder.buildConfigDrive(null, "teste", "C:", null);
|
||||
public void buildConfigDriveTestNoVmDataAndNic() {
|
||||
ConfigDriveBuilder.buildConfigDrive(null, null, "teste", "C:", null, null);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void buildConfigDriveTestIoException() {
|
||||
try (MockedStatic<ConfigDriveBuilder> configDriveBuilderMocked = Mockito.mockStatic(ConfigDriveBuilder.class)) {
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(nullable(File.class))).thenThrow(CloudRuntimeException.class);
|
||||
Mockito.when(ConfigDriveBuilder.buildConfigDrive(new ArrayList<>(), "teste", "C:", null)).thenCallRealMethod();
|
||||
ConfigDriveBuilder.buildConfigDrive(new ArrayList<>(), "teste", "C:", null);
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.writeVendorEmptyJsonFile(nullable(File.class))).thenThrow(CloudRuntimeException.class);
|
||||
Mockito.when(ConfigDriveBuilder.buildConfigDrive(null, new ArrayList<>(), "teste", "C:", null, supportedServices)).thenCallRealMethod();
|
||||
ConfigDriveBuilder.buildConfigDrive(null, new ArrayList<>(), "teste", "C:", null, supportedServices);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,22 +144,26 @@ public class ConfigDriveBuilderTest {
|
|||
public void buildConfigDriveTest() {
|
||||
try (MockedStatic<ConfigDriveBuilder> configDriveBuilderMocked = Mockito.mockStatic(ConfigDriveBuilder.class)) {
|
||||
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(Mockito.any(File.class))).then(invocationOnMock -> null);
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.writeVendorEmptyJsonFile(Mockito.any(File.class))).then(invocationOnMock -> null);
|
||||
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.writeVmMetadata(Mockito.anyList(), Mockito.anyString(), Mockito.any(File.class), anyMap())).then(invocationOnMock -> null);
|
||||
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.linkUserData((Mockito.anyString()))).then(invocationOnMock -> null);
|
||||
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.generateAndRetrieveIsoAsBase64Iso(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenAnswer(invocation -> "mockIsoDataBase64");
|
||||
//force execution of real method
|
||||
Mockito.when(ConfigDriveBuilder.buildConfigDrive(new ArrayList<>(), "teste", "C:", null)).thenCallRealMethod();
|
||||
|
||||
String returnedIsoData = ConfigDriveBuilder.buildConfigDrive(new ArrayList<>(), "teste", "C:", null);
|
||||
NicProfile mockedNicProfile = Mockito.mock(NicProfile.class);
|
||||
Mockito.when(mockedNicProfile.getId()).thenReturn(1L);
|
||||
|
||||
//force execution of real method
|
||||
Mockito.when(ConfigDriveBuilder.buildConfigDrive(List.of(mockedNicProfile), new ArrayList<>(), "teste", "C:", null, supportedServices)).thenCallRealMethod();
|
||||
|
||||
String returnedIsoData = ConfigDriveBuilder.buildConfigDrive(List.of(mockedNicProfile), new ArrayList<>(), "teste", "C:", null, supportedServices);
|
||||
|
||||
Assert.assertEquals("mockIsoDataBase64", returnedIsoData);
|
||||
|
||||
configDriveBuilderMocked.verify(() -> {
|
||||
ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(Mockito.any(File.class));
|
||||
ConfigDriveBuilder.writeVendorEmptyJsonFile(Mockito.any(File.class));
|
||||
ConfigDriveBuilder.writeVmMetadata(Mockito.anyList(), Mockito.anyString(), Mockito.any(File.class), anyMap());
|
||||
ConfigDriveBuilder.linkUserData(Mockito.anyString());
|
||||
ConfigDriveBuilder.generateAndRetrieveIsoAsBase64Iso(Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
|
||||
|
|
@ -153,23 +172,23 @@ public class ConfigDriveBuilderTest {
|
|||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void writeVendorAndNetworkEmptyJsonFileTestCannotCreateOpenStackFolder() {
|
||||
public void writeVendorEmptyJsonFileTestCannotCreateOpenStackFolder() {
|
||||
File folderFileMock = Mockito.mock(File.class);
|
||||
Mockito.doReturn(false).when(folderFileMock).mkdirs();
|
||||
|
||||
ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(folderFileMock);
|
||||
ConfigDriveBuilder.writeVendorEmptyJsonFile(folderFileMock);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void writeVendorAndNetworkEmptyJsonFileTest() {
|
||||
public void writeVendorEmptyJsonFileTest() {
|
||||
File folderFileMock = Mockito.mock(File.class);
|
||||
Mockito.doReturn(false).when(folderFileMock).mkdirs();
|
||||
|
||||
ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(folderFileMock);
|
||||
ConfigDriveBuilder.writeVendorEmptyJsonFile(folderFileMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeVendorAndNetworkEmptyJsonFileTestCreatingFolder() {
|
||||
public void writeVendorEmptyJsonFileTestCreatingFolder() {
|
||||
try (MockedStatic<ConfigDriveBuilder> configDriveBuilderMocked = Mockito.mockStatic(ConfigDriveBuilder.class)) {
|
||||
|
||||
File folderFileMock = Mockito.mock(File.class);
|
||||
|
|
@ -177,9 +196,9 @@ public class ConfigDriveBuilderTest {
|
|||
Mockito.doReturn(true).when(folderFileMock).mkdirs();
|
||||
|
||||
//force execution of real method
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(folderFileMock)).thenCallRealMethod();
|
||||
configDriveBuilderMocked.when(() -> ConfigDriveBuilder.writeVendorEmptyJsonFile(folderFileMock)).thenCallRealMethod();
|
||||
|
||||
ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(folderFileMock);
|
||||
ConfigDriveBuilder.writeVendorEmptyJsonFile(folderFileMock);
|
||||
|
||||
Mockito.verify(folderFileMock).exists();
|
||||
Mockito.verify(folderFileMock).mkdirs();
|
||||
|
|
@ -501,4 +520,143 @@ public class ConfigDriveBuilderTest {
|
|||
Mockito.verify(mkIsoProgramInMacOsFileMock, Mockito.times(1)).getCanonicalPath();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteNetworkData() throws Exception {
|
||||
// Setup
|
||||
NicProfile nicp = mock(NicProfile.class);
|
||||
Mockito.when(nicp.getId()).thenReturn(1L);
|
||||
|
||||
Mockito.when(nicp.getMacAddress()).thenReturn("00:00:00:00:00:00");
|
||||
Mockito.when(nicp.getMtu()).thenReturn(2000);
|
||||
|
||||
Mockito.when(nicp.getIPv4Address()).thenReturn("172.31.0.10");
|
||||
Mockito.when(nicp.getDeviceId()).thenReturn(1);
|
||||
Mockito.when(nicp.getIPv4Netmask()).thenReturn("255.255.255.0");
|
||||
Mockito.when(nicp.getUuid()).thenReturn("NETWORK UUID");
|
||||
Mockito.when(nicp.getIPv4Gateway()).thenReturn("172.31.0.1");
|
||||
|
||||
|
||||
Mockito.when(nicp.getIPv6Address()).thenReturn("2001:db8:0:1234:0:567:8:1");
|
||||
Mockito.when(nicp.getIPv6Cidr()).thenReturn("2001:db8:0:1234:0:567:8:1/64");
|
||||
Mockito.when(nicp.getIPv6Gateway()).thenReturn("2001:db8:0:1234:0:567:8::1");
|
||||
|
||||
Mockito.when(nicp.getIPv4Dns1()).thenReturn("8.8.8.8");
|
||||
Mockito.when(nicp.getIPv4Dns2()).thenReturn("1.1.1.1");
|
||||
Mockito.when(nicp.getIPv6Dns1()).thenReturn("2001:4860:4860::8888");
|
||||
Mockito.when(nicp.getIPv6Dns2()).thenReturn("2001:4860:4860::8844");
|
||||
|
||||
|
||||
List<Network.Service> services1 = Arrays.asList(Network.Service.Dhcp, Network.Service.Dns);
|
||||
|
||||
Map<Long, List<Network.Service>> supportedServices = new HashMap<>();
|
||||
supportedServices.put(1L, services1);
|
||||
|
||||
TemporaryFolder folder = new TemporaryFolder();
|
||||
folder.create();
|
||||
File openStackFolder = folder.newFolder("openStack");
|
||||
|
||||
// Expected JSON structure
|
||||
String expectedJson = "{" +
|
||||
" \"links\": [" +
|
||||
" {" +
|
||||
" \"ethernet_mac_address\": \"00:00:00:00:00:00\"," +
|
||||
" \"id\": \"eth1\"," +
|
||||
" \"mtu\": 2000," +
|
||||
" \"type\": \"phy\"" +
|
||||
" }" +
|
||||
" ]," +
|
||||
" \"networks\": [" +
|
||||
" {" +
|
||||
" \"id\": \"eth1\"," +
|
||||
" \"ip_address\": \"172.31.0.10\"," +
|
||||
" \"link\": \"eth1\"," +
|
||||
" \"netmask\": \"255.255.255.0\"," +
|
||||
" \"network_id\": \"NETWORK UUID\"," +
|
||||
" \"type\": \"ipv4\"," +
|
||||
" \"routes\": [" +
|
||||
" {" +
|
||||
" \"gateway\": \"172.31.0.1\"," +
|
||||
" \"netmask\": \"0.0.0.0\"," +
|
||||
" \"network\": \"0.0.0.0\"" +
|
||||
" }" +
|
||||
" ]" +
|
||||
" }," +
|
||||
" {" +
|
||||
" \"id\": \"eth1\"," +
|
||||
" \"ip_address\": \"2001:db8:0:1234:0:567:8:1\"," +
|
||||
" \"link\": \"eth1\"," +
|
||||
" \"netmask\": \"64\"," +
|
||||
" \"network_id\": \"NETWORK UUID\"," +
|
||||
" \"type\": \"ipv6\"," +
|
||||
" \"routes\": [" +
|
||||
" {" +
|
||||
" \"gateway\": \"2001:db8:0:1234:0:567:8::1\"," +
|
||||
" \"netmask\": \"0\"," +
|
||||
" \"network\": \"::\"" +
|
||||
" }" +
|
||||
" ]" +
|
||||
" }" +
|
||||
" ]," +
|
||||
" \"services\": [" +
|
||||
" {" +
|
||||
" \"address\": \"8.8.8.8\"," +
|
||||
" \"type\": \"dns\"" +
|
||||
" }," +
|
||||
" {" +
|
||||
" \"address\": \"1.1.1.1\"," +
|
||||
" \"type\": \"dns\"" +
|
||||
" }," +
|
||||
" {" +
|
||||
" \"address\": \"2001:4860:4860::8888\"," +
|
||||
" \"type\": \"dns\"" +
|
||||
" }," +
|
||||
" {" +
|
||||
" \"address\": \"2001:4860:4860::8844\"," +
|
||||
" \"type\": \"dns\"" +
|
||||
" }" +
|
||||
" ]" +
|
||||
"}";
|
||||
|
||||
// Action
|
||||
ConfigDriveBuilder.writeNetworkData(Arrays.asList(nicp), supportedServices, openStackFolder);
|
||||
|
||||
// Verify
|
||||
File networkDataFile = new File(openStackFolder, "network_data.json");
|
||||
String content = FileUtils.readFileToString(networkDataFile, StandardCharsets.UTF_8);
|
||||
JsonObject actualJson = new JsonParser().parse(content).getAsJsonObject();
|
||||
JsonObject expectedJsonObject = new JsonParser().parse(expectedJson).getAsJsonObject();
|
||||
|
||||
Assert.assertEquals(expectedJsonObject, actualJson);
|
||||
folder.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteNetworkDataEmptyJson() throws Exception {
|
||||
// Setup
|
||||
NicProfile nicp = mock(NicProfile.class);
|
||||
List<Network.Service> services1 = Collections.emptyList();
|
||||
|
||||
Map<Long, List<Network.Service>> supportedServices = new HashMap<>();
|
||||
supportedServices.put(1L, services1);
|
||||
|
||||
TemporaryFolder folder = new TemporaryFolder();
|
||||
folder.create();
|
||||
File openStackFolder = folder.newFolder("openStack");
|
||||
|
||||
// Expected JSON structure
|
||||
String expectedJson = "{}";
|
||||
|
||||
// Action
|
||||
ConfigDriveBuilder.writeNetworkData(Arrays.asList(nicp), supportedServices, openStackFolder);
|
||||
|
||||
// Verify
|
||||
File networkDataFile = new File(openStackFolder, "network_data.json");
|
||||
String content = FileUtils.readFileToString(networkDataFile, StandardCharsets.UTF_8);
|
||||
JsonObject actualJson = new JsonParser().parse(content).getAsJsonObject();
|
||||
JsonObject expectedJsonObject = new JsonParser().parse(expectedJson).getAsJsonObject();
|
||||
|
||||
Assert.assertEquals(expectedJsonObject, actualJson);
|
||||
folder.delete();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
// 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.storage.configdrive;
|
||||
|
||||
import static org.apache.cloudstack.storage.configdrive.ConfigDriveUtils.mergeJsonArraysAndUpdateObject;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ConfigDriveUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testMergeJsonArraysAndUpdateObjectWithEmptyObjects() {
|
||||
JsonObject finalObject = new JsonObject();
|
||||
JsonObject newObj = new JsonObject();
|
||||
mergeJsonArraysAndUpdateObject(finalObject, newObj, "links", "id", "type");
|
||||
Assert.assertEquals("{}", finalObject.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeJsonArraysAndUpdateObjectWithNewMembersAdded() {
|
||||
JsonObject finalObject = new JsonObject();
|
||||
|
||||
JsonObject newObj = new JsonObject();
|
||||
JsonArray newMembers = new JsonArray();
|
||||
JsonObject newMember = new JsonObject();
|
||||
newMember.addProperty("id", "eth0");
|
||||
newMember.addProperty("type", "phy");
|
||||
newMembers.add(newMember);
|
||||
newObj.add("links", newMembers);
|
||||
|
||||
mergeJsonArraysAndUpdateObject(finalObject, newObj, "links", "id", "type");
|
||||
Assert.assertEquals(1, finalObject.getAsJsonArray("links").size());
|
||||
JsonObject expectedObj = new JsonParser().parse("{'links': [{'id': 'eth0', 'type': 'phy'}]}").getAsJsonObject();
|
||||
Assert.assertEquals(expectedObj, finalObject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeJsonArraysAndUpdateObjectWithDuplicateMembersIgnored() {
|
||||
JsonObject finalObject = new JsonObject();
|
||||
JsonArray existingMembers = new JsonArray();
|
||||
JsonObject existingMember = new JsonObject();
|
||||
existingMember.addProperty("id", "eth0");
|
||||
existingMember.addProperty("type", "phy");
|
||||
existingMembers.add(existingMember);
|
||||
finalObject.add("links", existingMembers);
|
||||
|
||||
JsonObject newObj = new JsonObject();
|
||||
newObj.add("links", existingMembers); // same as existingMembers for duplication
|
||||
|
||||
mergeJsonArraysAndUpdateObject(finalObject, newObj, "links", "id", "type");
|
||||
Assert.assertEquals(1, finalObject.getAsJsonArray("links").size());
|
||||
JsonObject expectedObj = new JsonParser().parse("{'links': [{'id': 'eth0', 'type': 'phy'}]}").getAsJsonObject();
|
||||
Assert.assertEquals(expectedObj, finalObject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeJsonArraysAndUpdateObjectWithDifferentMembers() {
|
||||
JsonObject finalObject = new JsonObject();
|
||||
|
||||
JsonArray newMembers = new JsonArray();
|
||||
JsonObject newMember = new JsonObject();
|
||||
newMember.addProperty("id", "eth0");
|
||||
newMember.addProperty("type", "phy");
|
||||
newMembers.add(newMember);
|
||||
finalObject.add("links", newMembers);
|
||||
|
||||
JsonObject newObj = new JsonObject();
|
||||
newMembers = new JsonArray();
|
||||
newMember = new JsonObject();
|
||||
newMember.addProperty("id", "eth1");
|
||||
newMember.addProperty("type", "phy");
|
||||
newMembers.add(newMember);
|
||||
newObj.add("links", newMembers);
|
||||
|
||||
mergeJsonArraysAndUpdateObject(finalObject, newObj, "links", "id", "type");
|
||||
Assert.assertEquals(2, finalObject.getAsJsonArray("links").size());
|
||||
JsonObject expectedObj = new JsonParser().parse("{'links': [{'id': 'eth0', 'type': 'phy'}, {'id': 'eth1', 'type': 'phy'}]}").getAsJsonObject();
|
||||
Assert.assertEquals(expectedObj, finalObject);
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testMergeJsonArraysAndUpdateObjectWithNullObjects() {
|
||||
mergeJsonArraysAndUpdateObject(null, null, "services", "id", "type");
|
||||
}
|
||||
}
|
||||
|
|
@ -2174,7 +2174,6 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
NetworkVO network = _networksDao.findById(networkId);
|
||||
Integer networkRate = getNetworkRate(network.getId(), vm.getId());
|
||||
|
||||
// NetworkGuru guru = _networkGurus.get(network.getGuruName());
|
||||
NicProfile profile =
|
||||
new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, isSecurityGroupSupportedInNetwork(network), getNetworkTag(
|
||||
vm.getHypervisorType(), network));
|
||||
|
|
@ -2184,7 +2183,17 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
|
|||
if (network.getTrafficType() == TrafficType.Guest && network.getPrivateMtu() != null) {
|
||||
profile.setMtu(network.getPrivateMtu());
|
||||
}
|
||||
// guru.updateNicProfile(profile, network);
|
||||
|
||||
DataCenter dc = _dcDao.findById(network.getDataCenterId());
|
||||
|
||||
Pair<String, String> ip4Dns = getNetworkIp4Dns(network, dc);
|
||||
profile.setIPv4Dns1(ip4Dns.first());
|
||||
profile.setIPv4Dns2(ip4Dns.second());
|
||||
|
||||
Pair<String, String> ip6Dns = getNetworkIp6Dns(network, dc);
|
||||
profile.setIPv6Dns1(ip6Dns.first());
|
||||
profile.setIPv6Dns2(ip6Dns.second());
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
// under the License.
|
||||
package com.cloud.network.element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -24,6 +25,7 @@ import java.util.Set;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
|
|
@ -90,7 +92,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();
|
||||
|
|
@ -110,6 +113,8 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
|||
@Inject
|
||||
NetworkModel _networkModel;
|
||||
@Inject
|
||||
NetworkOrchestrationService _networkOrchestrationService;
|
||||
@Inject
|
||||
GuestOSCategoryDao _guestOSCategoryDao;
|
||||
@Inject
|
||||
GuestOSDao _guestOSDao;
|
||||
|
|
@ -197,6 +202,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, new HashMap<>());
|
||||
capabilities.put(Service.Dns, new HashMap<>());
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
|
|
@ -224,8 +231,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
|||
public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context)
|
||||
throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
|
||||
return (canHandle(network.getTrafficType())
|
||||
&& configureConfigDriveData(profile, nic, dest))
|
||||
&& createConfigDriveIso(profile, dest, null);
|
||||
&& configureConfigDriveData(profile, nic, dest));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -342,10 +348,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 +407,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 +537,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 +549,9 @@ 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);
|
||||
List<NicProfile> nicProfiles = _networkOrchestrationService.getNicProfiles(nic.getVirtualMachineId(), profile.getHypervisorType());
|
||||
final Map<Long, List<Service>> supportedServices = getSupportedServicesByElementForNetwork(nicProfiles);
|
||||
final String isoData = ConfigDriveBuilder.buildConfigDrive(nicProfiles, profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), customUserdataParamMap, supportedServices);
|
||||
final HandleConfigDriveIsoCommand configDriveIsoCommand = new HandleConfigDriveIsoCommand(isoPath, isoData, null, false, true, true);
|
||||
|
||||
final HandleConfigDriveIsoAnswer answer = (HandleConfigDriveIsoAnswer) agentManager.easySend(hostId, configDriveIsoCommand);
|
||||
|
|
@ -590,7 +601,27 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean createConfigDriveIso(VirtualMachineProfile profile, DeployDestination dest, DiskTO disk) throws ResourceUnavailableException {
|
||||
private Map<Long, List<Network.Service>> getSupportedServicesByElementForNetwork(List<NicProfile> nics) {
|
||||
|
||||
Map<Long, List<Network.Service>> supportedServices = new HashMap<>();
|
||||
for (NicProfile nic: nics) {
|
||||
ArrayList<Network.Service> serviceList = new ArrayList<>();
|
||||
if (_networkModel.isProviderSupportServiceInNetwork(nic.getNetworkId(), Service.Dns, getProvider())) {
|
||||
serviceList.add(Service.Dns);
|
||||
}
|
||||
if (_networkModel.isProviderSupportServiceInNetwork(nic.getNetworkId(), Service.UserData, getProvider())) {
|
||||
serviceList.add(Service.UserData);
|
||||
}
|
||||
if (_networkModel.isProviderSupportServiceInNetwork(nic.getNetworkId(), Service.Dhcp, getProvider())) {
|
||||
serviceList.add(Service.Dhcp);
|
||||
}
|
||||
supportedServices.put(nic.getId(), serviceList);
|
||||
}
|
||||
|
||||
return supportedServices;
|
||||
}
|
||||
|
||||
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 +636,10 @@ 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);
|
||||
List<NicProfile> nicProfiles = _networkOrchestrationService.getNicProfiles(nic.getVirtualMachineId(), profile.getHypervisorType());
|
||||
final Map<Long, List<Service>> supportedServices = getSupportedServicesByElementForNetwork(nicProfiles);
|
||||
final String isoData = ConfigDriveBuilder.buildConfigDrive(
|
||||
nicProfiles, profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), customUserdataParamMap, supportedServices);
|
||||
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 +792,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ import com.cloud.vm.dao.UserVmDetailsDao;
|
|||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
|
|
@ -83,6 +84,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -148,6 +150,7 @@ public class ConfigDriveNetworkElementTest {
|
|||
@Mock private AgentManager agentManager;
|
||||
@Mock private CallContext callContextMock;
|
||||
@Mock private DomainVO domainVO;
|
||||
@Mock private NetworkOrchestrationService _networkOrchestrationService;
|
||||
|
||||
@Spy @InjectMocks
|
||||
private ConfigDriveNetworkElement _configDrivesNetworkElement = new ConfigDriveNetworkElement();
|
||||
|
|
@ -264,13 +267,9 @@ public class ConfigDriveNetworkElementTest {
|
|||
try (MockedStatic<ConfigDriveBuilder> ignored1 = Mockito.mockStatic(ConfigDriveBuilder.class); MockedStatic<CallContext> ignored2 = Mockito.mockStatic(CallContext.class)) {
|
||||
Mockito.when(CallContext.current()).thenReturn(callContextMock);
|
||||
Mockito.doReturn(Mockito.mock(Account.class)).when(callContextMock).getCallingAccount();
|
||||
Mockito.when(ConfigDriveBuilder.buildConfigDrive(Mockito.anyList(), Mockito.anyString(), Mockito.anyString(), Mockito.anyMap())).thenReturn("content");
|
||||
|
||||
final HandleConfigDriveIsoAnswer answer = mock(HandleConfigDriveIsoAnswer.class);
|
||||
final UserVmDetailVO userVmDetailVO = mock(UserVmDetailVO.class);
|
||||
when(agentManager.easySend(Mockito.anyLong(), Mockito.any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
|
||||
when(answer.getResult()).thenReturn(true);
|
||||
when(answer.getConfigDriveLocation()).thenReturn(NetworkElement.Location.PRIMARY);
|
||||
when(network.getTrafficType()).thenReturn(Networks.TrafficType.Guest);
|
||||
when(virtualMachine.getUuid()).thenReturn("vm-uuid");
|
||||
when(userVmDetailVO.getValue()).thenReturn(PUBLIC_KEY);
|
||||
|
|
@ -288,6 +287,28 @@ public class ConfigDriveNetworkElementTest {
|
|||
profile.setConfigDriveLabel("testlabel");
|
||||
assertTrue(_configDrivesNetworkElement.addPasswordAndUserdata(
|
||||
network, nicp, profile, deployDestination, null));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateConfigDriveIso() throws Exception {
|
||||
try (MockedStatic<ConfigDriveBuilder> ignored1 = Mockito.mockStatic(ConfigDriveBuilder.class); MockedStatic<CallContext> ignored2 = Mockito.mockStatic(CallContext.class)) {
|
||||
Mockito.when(CallContext.current()).thenReturn(callContextMock);
|
||||
Mockito.when(ConfigDriveBuilder.buildConfigDrive(Mockito.anyList(), Mockito.anyList(), Mockito.anyString(), Mockito.anyString(), Mockito.anyMap(), Mockito.anyMap())).thenReturn("content");
|
||||
|
||||
final HandleConfigDriveIsoAnswer answer = mock(HandleConfigDriveIsoAnswer.class);
|
||||
when(agentManager.easySend(Mockito.anyLong(), Mockito.any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
|
||||
when(answer.getResult()).thenReturn(true);
|
||||
when(answer.getConfigDriveLocation()).thenReturn(NetworkElement.Location.PRIMARY);
|
||||
when(virtualMachine.getUuid()).thenReturn("vm-uuid");
|
||||
|
||||
Map<VirtualMachineProfile.Param, Object> parms = Maps.newHashMap();
|
||||
parms.put(VirtualMachineProfile.Param.VmPassword, PASSWORD);
|
||||
parms.put(VirtualMachineProfile.Param.VmSshPubKey, PUBLIC_KEY);
|
||||
VirtualMachineProfile profile = new VirtualMachineProfileImpl(virtualMachine, null, serviceOfferingVO, null, parms);
|
||||
profile.setConfigDriveLabel("testlabel");
|
||||
profile.setVmData(Collections.emptyList());
|
||||
assertTrue(_configDrivesNetworkElement.createConfigDriveIso(nicp, profile, deployDestination, null));
|
||||
|
||||
ArgumentCaptor<HandleConfigDriveIsoCommand> commandCaptor = ArgumentCaptor.forClass(HandleConfigDriveIsoCommand.class);
|
||||
verify(agentManager, times(1)).easySend(Mockito.anyLong(), commandCaptor.capture());
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import javax.inject.Inject;
|
|||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.network.PublicIpQuarantine;
|
||||
import com.cloud.network.VirtualRouterProvider;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
|
|
@ -640,6 +641,11 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NicProfile> getNicProfiles(Long vmId, Hypervisor.HypervisorType hypervisorType) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getSystemVMAccessDetails(VirtualMachine vm) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
# under the License.
|
||||
""" BVT tests for Network Life Cycle
|
||||
"""
|
||||
import json
|
||||
|
||||
# Import Local Modules
|
||||
from marvin.codes import (FAILED, STATIC_NAT_RULE, LB_RULE,
|
||||
NAT_RULE, PASS)
|
||||
|
|
@ -24,7 +26,7 @@ from marvin.cloudstackTestCase import cloudstackTestCase
|
|||
from marvin.cloudstackException import CloudstackAPIException
|
||||
from marvin.cloudstackAPI import rebootRouter
|
||||
from marvin.sshClient import SshClient
|
||||
from marvin.lib.utils import cleanup_resources, get_process_status, get_host_credentials
|
||||
from marvin.lib.utils import cleanup_resources, get_process_status, get_host_credentials, random_gen
|
||||
from marvin.lib.base import (Account,
|
||||
VirtualMachine,
|
||||
ServiceOffering,
|
||||
|
|
@ -37,7 +39,9 @@ from marvin.lib.base import (Account,
|
|||
LoadBalancerRule,
|
||||
Router,
|
||||
NIC,
|
||||
Cluster)
|
||||
Template,
|
||||
Cluster,
|
||||
SSHKeyPair)
|
||||
from marvin.lib.common import (get_domain,
|
||||
get_free_vlan,
|
||||
get_zone,
|
||||
|
|
@ -58,9 +62,11 @@ from marvin.lib.decoratorGenerators import skipTestIf
|
|||
from ddt import ddt, data
|
||||
import unittest
|
||||
# Import System modules
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
import random
|
||||
import tempfile
|
||||
|
||||
_multiprocess_shared_ = True
|
||||
|
||||
|
|
@ -2113,3 +2119,313 @@ class TestSharedNetwork(cloudstackTestCase):
|
|||
0,
|
||||
"Failed to find the placeholder IP"
|
||||
)
|
||||
|
||||
|
||||
class TestSharedNetworkWithConfigDrive(cloudstackTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.testClient = super(TestSharedNetworkWithConfigDrive, cls).getClsTestClient()
|
||||
cls.apiclient = cls.testClient.getApiClient()
|
||||
|
||||
cls.services = cls.testClient.getParsedTestDataConfig()
|
||||
# Get Zone, Domain and templates
|
||||
cls.domain = get_domain(cls.apiclient)
|
||||
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
||||
cls.hv = cls.testClient.getHypervisorInfo()
|
||||
|
||||
if cls.hv.lower() == 'simulator':
|
||||
cls.skip = True
|
||||
return
|
||||
else:
|
||||
cls.skip = False
|
||||
|
||||
cls._cleanup = []
|
||||
|
||||
template = Template.register(
|
||||
cls.apiclient,
|
||||
cls.services["test_templates_cloud_init"][cls.hv.lower()],
|
||||
zoneid=cls.zone.id,
|
||||
hypervisor=cls.hv,
|
||||
)
|
||||
template.download(cls.apiclient)
|
||||
cls._cleanup.append(template)
|
||||
|
||||
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
|
||||
cls.services["virtual_machine"]["template"] = template.id
|
||||
cls.services["virtual_machine"]["username"] = "ubuntu"
|
||||
# Create Network Offering
|
||||
cls.services["shared_network_offering_configdrive"]["specifyVlan"] = "True"
|
||||
cls.services["shared_network_offering_configdrive"]["specifyIpRanges"] = "True"
|
||||
cls.shared_network_offering = NetworkOffering.create(cls.apiclient,
|
||||
cls.services["shared_network_offering_configdrive"],
|
||||
conservemode=True)
|
||||
|
||||
cls.isolated_network_offering = NetworkOffering.create(
|
||||
cls.apiclient,
|
||||
cls.services["isolated_network_offering"],
|
||||
conservemode=True
|
||||
)
|
||||
|
||||
# Update network offering state from disabled to enabled.
|
||||
NetworkOffering.update(
|
||||
cls.isolated_network_offering,
|
||||
cls.apiclient,
|
||||
id=cls.isolated_network_offering.id,
|
||||
state="enabled"
|
||||
)
|
||||
|
||||
# Update network offering state from disabled to enabled.
|
||||
NetworkOffering.update(cls.shared_network_offering, cls.apiclient, state="enabled")
|
||||
|
||||
cls.service_offering = ServiceOffering.create(cls.apiclient, cls.services["service_offering"])
|
||||
physical_network, vlan = get_free_vlan(cls.apiclient, cls.zone.id)
|
||||
# create network using the shared network offering created
|
||||
|
||||
cls.services["shared_network"]["acltype"] = "domain"
|
||||
cls.services["shared_network"]["vlan"] = vlan
|
||||
cls.services["shared_network"]["networkofferingid"] = cls.shared_network_offering.id
|
||||
cls.services["shared_network"]["physicalnetworkid"] = physical_network.id
|
||||
|
||||
cls.setSharedNetworkParams("shared_network")
|
||||
cls.shared_network = Network.create(cls.apiclient,
|
||||
cls.services["shared_network"],
|
||||
networkofferingid=cls.shared_network_offering.id,
|
||||
zoneid=cls.zone.id)
|
||||
|
||||
cls.isolated_network = Network.create(
|
||||
cls.apiclient,
|
||||
cls.services["isolated_network"],
|
||||
networkofferingid=cls.isolated_network_offering.id,
|
||||
zoneid=cls.zone.id
|
||||
)
|
||||
|
||||
cls._cleanup.extend([
|
||||
cls.service_offering,
|
||||
cls.shared_network,
|
||||
cls.shared_network_offering,
|
||||
cls.isolated_network,
|
||||
cls.isolated_network_offering,
|
||||
])
|
||||
cls.tmp_files = []
|
||||
cls.keypair = cls.generate_ssh_keys()
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def generate_ssh_keys(cls):
|
||||
"""Generates ssh key pair
|
||||
|
||||
Writes the private key into a temp file and returns the file name
|
||||
|
||||
:returns: generated keypair
|
||||
:rtype: MySSHKeyPair
|
||||
"""
|
||||
cls.keypair = SSHKeyPair.create(
|
||||
cls.apiclient,
|
||||
name=random_gen() + ".pem")
|
||||
|
||||
cls._cleanup.append(SSHKeyPair(cls.keypair.__dict__, None))
|
||||
cls.debug("Created keypair with name: %s" % cls.keypair.name)
|
||||
cls.debug("Writing the private key to local file")
|
||||
pkfile = tempfile.gettempdir() + os.sep + cls.keypair.name
|
||||
cls.keypair.private_key_file = pkfile
|
||||
cls.tmp_files.append(pkfile)
|
||||
cls.debug("File path: %s" % pkfile)
|
||||
with open(pkfile, "w+") as f:
|
||||
f.write(cls.keypair.privatekey)
|
||||
os.chmod(pkfile, 0o400)
|
||||
|
||||
return cls.keypair
|
||||
|
||||
def setUp(self):
|
||||
self.apiclient = self.testClient.getApiClient()
|
||||
self.dbclient = self.testClient.getDbConnection()
|
||||
if self.skip:
|
||||
self.skipTest("Hypervisor is simulator - skipping Test..")
|
||||
self.cleanup = []
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
try:
|
||||
# Cleanup resources used
|
||||
cleanup_resources(cls.apiclient, cls._cleanup)
|
||||
for tmp_file in cls.tmp_files:
|
||||
os.remove(tmp_file)
|
||||
except Exception as e:
|
||||
raise Exception("Warning: Exception during cleanup : %s" % e)
|
||||
return
|
||||
|
||||
def tearDown(self):
|
||||
cleanup_resources(self.apiclient, self.cleanup)
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def setSharedNetworkParams(cls, network, range=20):
|
||||
|
||||
# @range: range decides the endip. Pass the range as "x" if you want the difference between the startip
|
||||
# and endip as "x"
|
||||
# Set the subnet number of shared networks randomly prior to execution
|
||||
# of each test case to avoid overlapping of ip addresses
|
||||
shared_network_subnet_number = random.randrange(1, 254)
|
||||
cls.services[network]["gateway"] = "172.16." + str(shared_network_subnet_number) + ".1"
|
||||
cls.services[network]["startip"] = "172.16." + str(shared_network_subnet_number) + ".2"
|
||||
cls.services[network]["endip"] = "172.16." + str(shared_network_subnet_number) + "." + str(range + 1)
|
||||
cls.services[network]["netmask"] = "255.255.255.0"
|
||||
logger.debug("Executing command '%s'" % cls.services[network])
|
||||
|
||||
def _mount_config_drive(self, ssh):
|
||||
"""
|
||||
This method is to verify whether configdrive iso
|
||||
is attached to vm or not
|
||||
Returns mount path if config drive is attached else None
|
||||
"""
|
||||
mountdir = "/root/iso"
|
||||
cmd = "sudo blkid -t LABEL='config-2' " \
|
||||
"/dev/sr? /dev/hd? /dev/sd? /dev/xvd? -o device"
|
||||
tmp_cmd = [
|
||||
'sudo bash -c "if [ ! -d {0} ]; then mkdir {0}; fi"'.format(mountdir),
|
||||
"sudo umount %s" % mountdir]
|
||||
self.debug("Unmounting drive from %s" % mountdir)
|
||||
for tcmd in tmp_cmd:
|
||||
ssh.execute(tcmd)
|
||||
|
||||
self.debug("Trying to find ConfigDrive device")
|
||||
configDrive = ssh.execute(cmd)
|
||||
if not configDrive:
|
||||
self.warn("ConfigDrive is not attached")
|
||||
return None
|
||||
|
||||
res = ssh.execute("sudo mount {} {}".format(str(configDrive[0]), mountdir))
|
||||
if str(res).lower().find("read-only") > -1:
|
||||
self.debug("ConfigDrive iso is mounted at location %s" % mountdir)
|
||||
return mountdir
|
||||
else:
|
||||
return None
|
||||
|
||||
def _umount_config_drive(self, ssh, mount_path):
|
||||
"""unmount config drive inside guest vm
|
||||
|
||||
:param ssh: SSH connection to the VM
|
||||
:type ssh: marvin.sshClient.SshClient
|
||||
:type mount_path: str
|
||||
"""
|
||||
ssh.execute("sudo umount -d %s" % mount_path)
|
||||
# Give the VM time to unlock the iso device
|
||||
time.sleep(0.5)
|
||||
# Verify umount
|
||||
result = ssh.execute("sudo ls %s" % mount_path)
|
||||
self.assertTrue(len(result) == 0,
|
||||
"After umount directory should be empty "
|
||||
"but contains: %s" % result)
|
||||
|
||||
def _get_config_drive_data(self, ssh, file, name, fail_on_missing=True):
|
||||
"""Fetches the content of a file file on the config drive
|
||||
|
||||
:param ssh: SSH connection to the VM
|
||||
:param file: path to the file to fetch
|
||||
:param name: description of the file
|
||||
:param fail_on_missing:
|
||||
whether the test should fail if the file is missing
|
||||
:type ssh: marvin.sshClient.SshClient
|
||||
:type file: str
|
||||
:type name: str
|
||||
:type fail_on_missing: bool
|
||||
:returns: the content of the file
|
||||
:rtype: str
|
||||
"""
|
||||
cmd = "sudo cat %s" % file
|
||||
res = ssh.execute(cmd)
|
||||
content = '\n'.join(res)
|
||||
|
||||
if fail_on_missing and "No such file or directory" in content:
|
||||
self.debug("{} is not found".format(name))
|
||||
self.fail("{} is not found".format(name))
|
||||
|
||||
return content
|
||||
|
||||
def _get_ip_address_output(self, ssh):
|
||||
cmd = "ip address"
|
||||
res = ssh.execute(cmd)
|
||||
return '\n'.join(res)
|
||||
|
||||
@attr(tags=["advanced", "shared"], required_hardware="true")
|
||||
def test_01_deployVMInSharedNetwork(self):
|
||||
try:
|
||||
self.virtual_machine = VirtualMachine.create(self.apiclient, self.services["virtual_machine"],
|
||||
networkids=[self.shared_network.id, self.isolated_network.id],
|
||||
serviceofferingid=self.service_offering.id,
|
||||
keypair=self.keypair.name
|
||||
)
|
||||
self.cleanup.append(self.virtual_machine)
|
||||
except Exception as e:
|
||||
self.fail("Exception while deploying virtual machine: %s" % e)
|
||||
|
||||
public_ips = list_publicIP(
|
||||
self.apiclient,
|
||||
associatednetworkid=self.isolated_network.id
|
||||
)
|
||||
public_ip = public_ips[0]
|
||||
FireWallRule.create(
|
||||
self.apiclient,
|
||||
ipaddressid=public_ip.id,
|
||||
protocol=self.services["natrule"]["protocol"],
|
||||
cidrlist=['0.0.0.0/0'],
|
||||
startport=self.services["natrule"]["publicport"],
|
||||
endport=self.services["natrule"]["publicport"]
|
||||
)
|
||||
|
||||
nat_rule = NATRule.create(
|
||||
self.apiclient,
|
||||
self.virtual_machine,
|
||||
self.services["natrule"],
|
||||
public_ip.id
|
||||
)
|
||||
|
||||
private_key_file_location = self.keypair.private_key_file if self.keypair else None
|
||||
ssh = self.virtual_machine.get_ssh_client(ipaddress=nat_rule.ipaddress,
|
||||
keyPairFileLocation=private_key_file_location, retries=5)
|
||||
|
||||
mount_path = self._mount_config_drive(ssh)
|
||||
|
||||
network_data_content = self._get_config_drive_data(ssh, mount_path + "/openstack/latest/network_data.json",
|
||||
"network_data")
|
||||
|
||||
network_data = json.loads(network_data_content)
|
||||
|
||||
self._umount_config_drive(ssh, mount_path)
|
||||
|
||||
ip_address_output = self._get_ip_address_output(ssh)
|
||||
|
||||
self.assertTrue('links' in network_data, "network_data.json doesn't contain links")
|
||||
self.assertTrue('networks' in network_data, "network_data.json doesn't contain networks")
|
||||
self.assertTrue('services' in network_data, "network_data.json doesn't contain services")
|
||||
|
||||
for x in ['links', 'networks', 'services']:
|
||||
self.assertTrue(x in network_data, "network_data.json doesn't contain " + x)
|
||||
self.assertEqual(len(network_data[x]), 2, "network_data.json doesn't contain 2 " + x)
|
||||
|
||||
self.assertIn(network_data['links'][0]['ethernet_mac_address'],
|
||||
[self.virtual_machine.nic[0].macaddress, self.virtual_machine.nic[1].macaddress],
|
||||
"macaddress doesn't match")
|
||||
self.assertIn(network_data['links'][1]['ethernet_mac_address'],
|
||||
[self.virtual_machine.nic[0].macaddress, self.virtual_machine.nic[1].macaddress],
|
||||
"macaddress doesn't match")
|
||||
|
||||
self.assertIn(network_data['networks'][0]['ip_address'],
|
||||
[self.virtual_machine.nic[0].ipaddress, self.virtual_machine.nic[1].ipaddress],
|
||||
"ip address doesn't match")
|
||||
self.assertIn(network_data['networks'][1]['ip_address'],
|
||||
[self.virtual_machine.nic[0].ipaddress, self.virtual_machine.nic[1].ipaddress],
|
||||
"ip address doesn't match")
|
||||
self.assertIn(network_data['networks'][0]['netmask'],
|
||||
[self.virtual_machine.nic[0].netmask, self.virtual_machine.nic[1].netmask],
|
||||
"netmask doesn't match")
|
||||
self.assertIn(network_data['networks'][1]['netmask'],
|
||||
[self.virtual_machine.nic[0].netmask, self.virtual_machine.nic[1].netmask],
|
||||
"netmask doesn't match")
|
||||
|
||||
self.assertEqual(network_data['services'][0]['type'], 'dns', "network_data.json doesn't contain dns service")
|
||||
self.assertEqual(network_data['services'][1]['type'], 'dns', "network_data.json doesn't contain dns service")
|
||||
|
||||
self.assertTrue(self.virtual_machine.nic[0].ipaddress in ip_address_output, "ip address doesn't match")
|
||||
self.assertTrue(self.virtual_machine.nic[1].ipaddress in ip_address_output, "ip address doesn't match")
|
||||
|
|
|
|||
|
|
@ -450,6 +450,21 @@ test_data = {
|
|||
"UserData": "VirtualRouter"
|
||||
}
|
||||
},
|
||||
"shared_network_offering_configdrive": {
|
||||
"name": "MySharedOfferingWithConfigDrive-shared",
|
||||
"displaytext": "MySharedOfferingWithConfigDrive",
|
||||
"guestiptype": "Shared",
|
||||
"supportedservices": "Dhcp,Dns,UserData",
|
||||
"specifyVlan": "False",
|
||||
"specifyIpRanges": "False",
|
||||
"traffictype": "GUEST",
|
||||
"tags": "native",
|
||||
"serviceProviderList": {
|
||||
"Dhcp": "ConfigDrive",
|
||||
"Dns": "ConfigDrive",
|
||||
"UserData": "ConfigDrive"
|
||||
}
|
||||
},
|
||||
"shared_network_offering_all_services": {
|
||||
"name": "shared network offering with services enabled",
|
||||
"displaytext": "Shared network offering",
|
||||
|
|
@ -1047,6 +1062,41 @@ test_data = {
|
|||
"isextractable": "True"
|
||||
},
|
||||
},
|
||||
"test_templates_cloud_init": {
|
||||
"kvm": {
|
||||
"name": "ubuntu 22.04 kvm",
|
||||
"displaytext": "ubuntu 22.04 kvm",
|
||||
"format": "raw",
|
||||
"hypervisor": "kvm",
|
||||
"ostype": "Other Linux (64-bit)",
|
||||
"url": "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img",
|
||||
"requireshvm": "True",
|
||||
"ispublic": "True",
|
||||
"isextractable": "False"
|
||||
},
|
||||
"xenserver": {
|
||||
"name": "ubuntu 22.04 xen",
|
||||
"displaytext": "ubuntu 22.04 xen",
|
||||
"format": "vhd",
|
||||
"hypervisor": "xenserver",
|
||||
"ostype": "Other Linux (64-bit)",
|
||||
"url": "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64-azure.vhd.tar.gz",
|
||||
"requireshvm": "True",
|
||||
"ispublic": "True",
|
||||
"isextractable": "True"
|
||||
},
|
||||
"vmware": {
|
||||
"name": "ubuntu 22.04 vmware",
|
||||
"displaytext": "ubuntu 22.04 vmware",
|
||||
"format": "ova",
|
||||
"hypervisor": "vmware",
|
||||
"ostype": "Other Linux (64-bit)",
|
||||
"url": "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.ova",
|
||||
"requireshvm": "True",
|
||||
"ispublic": "True",
|
||||
"deployasis": "True"
|
||||
},
|
||||
},
|
||||
"test_ovf_templates": [
|
||||
{
|
||||
"name": "test-ovf",
|
||||
|
|
|
|||
Loading…
Reference in New Issue