diff --git a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java index ed02dcb9c67..977e27eb14f 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java @@ -56,6 +56,7 @@ public interface VirtualMachineProfile { public static final Param VmSshPubKey = new Param("VmSshPubKey"); public static final Param ControlNic = new Param("ControlNic"); public static final Param ReProgramGuestNetworks = new Param("RestartNetwork"); + public static final Param RollingRestart = new Param("RollingRestart"); public static final Param PxeSeverType = new Param("PxeSeverType"); public static final Param HaTag = new Param("HaTag"); public static final Param HaOperation = new Param("HaOperation"); @@ -173,4 +174,6 @@ public interface VirtualMachineProfile { Float getMemoryOvercommitRatio(); + boolean isRollingRestart(); + } diff --git a/engine/components-api/src/main/java/com/cloud/vm/VirtualMachineProfileImpl.java b/engine/components-api/src/main/java/com/cloud/vm/VirtualMachineProfileImpl.java index 0afca735a13..4d03396c1cb 100644 --- a/engine/components-api/src/main/java/com/cloud/vm/VirtualMachineProfileImpl.java +++ b/engine/components-api/src/main/java/com/cloud/vm/VirtualMachineProfileImpl.java @@ -263,6 +263,11 @@ public class VirtualMachineProfileImpl implements VirtualMachineProfile { return memoryOvercommitRatio; } + @Override + public boolean isRollingRestart() { + return Boolean.TRUE.equals(getParameter(VirtualMachineProfile.Param.RollingRestart)); + } + @Override public List getVmData() { return vmData; diff --git a/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/ConfigDrive.java b/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/ConfigDrive.java index ec461991537..07cfdc883ca 100644 --- a/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/ConfigDrive.java +++ b/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/ConfigDrive.java @@ -26,11 +26,12 @@ public class ConfigDrive { public static final String openStackConfigDriveName = "/openstack/latest/"; /** - * This is the path to iso file relative to mount point - * @return config drive iso file path + * Creates the path to ISO file relative to mount point. + * The config driver path will have the following formated: {@link #CONFIGDRIVEDIR} + / + instanceName + / + {@link #CONFIGDRIVEFILENAME} + * + * @return config drive ISO file path */ - public static String createConfigDrivePath(final String instanceName) { + public static String createConfigDrivePath(String instanceName) { return ConfigDrive.CONFIGDRIVEDIR + "/" + instanceName + "/" + ConfigDrive.CONFIGDRIVEFILENAME; } - -} +} \ No newline at end of file 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 d847aa1d1d7..dc6b8893495 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 @@ -23,9 +23,7 @@ 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 java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -42,7 +40,6 @@ import org.joda.time.Duration; import com.cloud.network.NetworkModel; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; -import com.google.common.base.Strings; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -51,30 +48,45 @@ public class ConfigDriveBuilder { public static final Logger LOG = Logger.getLogger(ConfigDriveBuilder.class); - private static void writeFile(final File folder, final String file, final String content) { - if (folder == null || Strings.isNullOrEmpty(file)) { - return; - } - final File vendorDataFile = new File(folder, file); - try (final FileWriter fw = new FileWriter(vendorDataFile); final BufferedWriter bw = new BufferedWriter(fw)) { - bw.write(content); + /** + * Writes a content {@link String} to a file that is going to be created in a folder. We will not append to the file if it already exists. Therefore, its content will be overwritten. + * Moreover, the charset used is {@link com.cloud.utils.StringUtils#getPreferredCharset()}. + * + * We expect the folder object and the file not to be null/empty. + */ + static void writeFile(File folder, String file, String content) { + try { + FileUtils.write(new File(folder, file), content, com.cloud.utils.StringUtils.getPreferredCharset(), false); } catch (IOException ex) { throw new CloudRuntimeException("Failed to create config drive file " + file, ex); } } - public static String fileToBase64String(final File isoFile) throws IOException { + /** + * 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} + */ + public static String fileToBase64String(File isoFile) throws IOException { byte[] encoded = Base64.encodeBase64(FileUtils.readFileToByteArray(isoFile)); return new String(encoded, StandardCharsets.US_ASCII); } - public static File base64StringToFile(final String encodedIsoData, final String folder, final String fileName) throws IOException { + /** + * Writes a String encoded in base 64 to a file in the given folder. + * The content will be decoded and then written to the file. Be aware that we will overwrite the content of the file if it already exists. + * Moreover, the content will must be encoded in {@link StandardCharsets#US_ASCII} before it is encoded in base 64. + */ + public static File base64StringToFile(String encodedIsoData, String folder, String fileName) throws IOException { byte[] decoded = Base64.decodeBase64(encodedIsoData.getBytes(StandardCharsets.US_ASCII)); Path destPath = Paths.get(folder, fileName); return Files.write(destPath, decoded).toFile(); } - public static String buildConfigDrive(final List vmData, final String isoFileName, final String driveLabel) { + /** + * 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 vmData, String isoFileName, String driveLabel) { if (vmData == null) { throw new CloudRuntimeException("No VM metadata provided"); } @@ -86,103 +98,180 @@ public class ConfigDriveBuilder { tempDirName = tempDir.toString(); File openStackFolder = new File(tempDirName + ConfigDrive.openStackConfigDriveName); - if (openStackFolder.exists() || openStackFolder.mkdirs()) { - writeFile(openStackFolder, "vendor_data.json", "{}"); - writeFile(openStackFolder, "network_data.json", "{}"); - } else { - throw new CloudRuntimeException("Failed to create folder " + openStackFolder); - } - JsonObject metaData = new JsonObject(); - for (String[] item : vmData) { - String dataType = item[CONFIGDATA_DIR]; - String fileName = item[CONFIGDATA_FILE]; - String content = item[CONFIGDATA_CONTENT]; - LOG.debug(String.format("[createConfigDriveIsoForVM] dataType=%s, filename=%s, content=%s", - dataType, fileName, (fileName.equals(PASSWORD_FILE)?"********":content))); + writeVendorAndNetworkEmptyJsonFile(openStackFolder); + writeVmMetadata(vmData, tempDirName, openStackFolder); - // create file with content in folder - if (dataType != null && !dataType.isEmpty()) { - //create folder - File typeFolder = new File(tempDirName + ConfigDrive.cloudStackConfigDriveName + dataType); - if (typeFolder.exists() || typeFolder.mkdirs()) { - if (StringUtils.isNotEmpty(content)) { - File file = new File(typeFolder, fileName + ".txt"); - try { - if (fileName.equals(USERDATA_FILE)) { - // User Data is passed as a base64 encoded string - FileUtils.writeByteArrayToFile(file, Base64.decodeBase64(content)); - } else { - FileUtils.write(file, content, com.cloud.utils.StringUtils.getPreferredCharset()); - } - } catch (IOException ex) { - throw new CloudRuntimeException("Failed to create file ", ex); - } - } - } else { - throw new CloudRuntimeException("Failed to create folder: " + typeFolder); - } + linkUserData(tempDirName); - //now write the file to the OpenStack directory - metaData = buildOpenStackMetaData(metaData, dataType, fileName, content); - } - } - writeFile(openStackFolder, "meta_data.json", metaData.toString()); - - String linkResult = linkUserData(tempDirName); - if (linkResult != null) { - String errMsg = "Unable to create user_data link due to " + linkResult; - throw new CloudRuntimeException(errMsg); - } - - File tmpIsoStore = new File(tempDirName, new File(isoFileName).getName()); - Script command = new Script("/usr/bin/genisoimage", Duration.standardSeconds(300), LOG); - command.add("-o", tmpIsoStore.getAbsolutePath()); - command.add("-ldots"); - command.add("-allow-lowercase"); - command.add("-allow-multidot"); - command.add("-cache-inodes"); // Enable caching inode and device numbers to find hard links to files. - command.add("-l"); - command.add("-quiet"); - command.add("-J"); - command.add("-r"); - command.add("-V", driveLabel); - command.add(tempDirName); - LOG.debug("Executing config drive creation command: " + command.toString()); - String result = command.execute(); - if (result != null) { - String errMsg = "Unable to create iso file: " + isoFileName + " due to " + result; - LOG.warn(errMsg); - throw new CloudRuntimeException(errMsg); - } - File tmpIsoFile = new File(tmpIsoStore.getAbsolutePath()); - // Max allowed file size of config drive is 64MB: https://docs.openstack.org/project-install-guide/baremetal/draft/configdrive.html - if (tmpIsoFile.length() > (64L * 1024L * 1024L)) { - throw new CloudRuntimeException("Config drive file exceeds maximum allowed size of 64MB"); - } - return fileToBase64String(tmpIsoFile); + return generateAndRetrieveIsoAsBase64Iso(isoFileName, driveLabel, tempDirName); } catch (IOException e) { throw new CloudRuntimeException("Failed due to", e); } finally { - try { - FileUtils.deleteDirectory(tempDir.toFile()); - } catch (IOException ioe) { - LOG.warn("Failed to delete ConfigDrive temporary directory: " + tempDirName, ioe); - } + deleteTempDir(tempDir); } } - private static String linkUserData(String tempDirName) { - //Hard link the user_data.txt file with the user_data file in the OpenStack directory. + private static void deleteTempDir(Path tempDir) { + try { + if (tempDir != null) { + FileUtils.deleteDirectory(tempDir.toFile()); + } + } catch (IOException ioe) { + LOG.warn("Failed to delete ConfigDrive temporary directory: " + tempDir.toString(), ioe); + } + } + + /** + * Generates the ISO file that has the tempDir content. + * + * Max allowed file size of config drive is 64MB [1]. Therefore, if the ISO is bigger than that, we throw a {@link CloudRuntimeException}. + * [1] https://docs.openstack.org/project-install-guide/baremetal/draft/configdrive.html + */ + static String generateAndRetrieveIsoAsBase64Iso(String isoFileName, String driveLabel, String tempDirName) throws IOException { + File tmpIsoStore = new File(tempDirName, isoFileName); + Script command = new Script(getProgramToGenerateIso(), Duration.standardSeconds(300), LOG); + command.add("-o", tmpIsoStore.getAbsolutePath()); + command.add("-ldots"); + command.add("-allow-lowercase"); + command.add("-allow-multidot"); + command.add("-cache-inodes"); // Enable caching inode and device numbers to find hard links to files. + command.add("-l"); + command.add("-quiet"); + command.add("-J"); + command.add("-r"); + command.add("-V", driveLabel); + command.add(tempDirName); + LOG.debug("Executing config drive creation command: " + command.toString()); + String result = command.execute(); + if (StringUtils.isNotBlank(result)) { + String errMsg = "Unable to create iso file: " + isoFileName + " due to ge" + result; + LOG.warn(errMsg); + throw new CloudRuntimeException(errMsg); + } + File tmpIsoFile = new File(tmpIsoStore.getAbsolutePath()); + if (tmpIsoFile.length() > (64L * 1024L * 1024L)) { + throw new CloudRuntimeException("Config drive file exceeds maximum allowed size of 64MB"); + } + return fileToBase64String(tmpIsoFile); + } + + /** + * Checks if the 'genisoimage' or 'mkisofs' is available and return the full qualified path for the program. + * The path checked are the following: + *
    + *
  • /usr/bin/genisoimage + *
  • /usr/bin/mkisofs + *
/usr/local/bin/mkisofs + */ + static String getProgramToGenerateIso() throws IOException { + File isoCreator = new File("/usr/bin/genisoimage"); + if (!isoCreator.exists()) { + isoCreator = new File("/usr/bin/mkisofs"); + if (!isoCreator.exists()) { + isoCreator = new File("/usr/local/bin/mkisofs"); + } + } + if (!isoCreator.exists()) { + throw new CloudRuntimeException("Cannot create iso for config drive using any know tool. Known paths [/usr/bin/genisoimage, /usr/bin/mkisofs, /usr/local/bin/mkisofs]"); + } + if (!isoCreator.canExecute()) { + throw new CloudRuntimeException("Cannot create iso for config drive using: " + isoCreator.getCanonicalPath()); + } + return isoCreator.getCanonicalPath(); + } + + /** + * First we generate a JSON object using {@link #createJsonObjectWithVmData(List, String)}, then we write it to a file called "meta_data.json". + */ + static void writeVmMetadata(List vmData, String tempDirName, File openStackFolder) { + JsonObject metaData = createJsonObjectWithVmData(vmData, tempDirName); + writeFile(openStackFolder, "meta_data.json", metaData.toString()); + } + + /** + * Writes the following empty JSON files: + *
    + *
  • vendor_data.json + *
  • network_data.json + *
+ * + * If the folder does not exist and we cannot create it, we throw a {@link CloudRuntimeException}. + */ + static void writeVendorAndNetworkEmptyJsonFile(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); + } + } + + /** + * Creates the {@link JsonObject} with VM's metadata. The vmData is a list of arrays; we expect this list to have the following entries: + *
    + *
  • [0]: config data type + *
  • [1]: config data file name + *
  • [2]: config data file content + *
+ */ + static JsonObject createJsonObjectWithVmData(List vmData, String tempDirName) { + JsonObject metaData = new JsonObject(); + for (String[] item : vmData) { + String dataType = item[CONFIGDATA_DIR]; + String fileName = item[CONFIGDATA_FILE]; + String content = item[CONFIGDATA_CONTENT]; + LOG.debug(String.format("[createConfigDriveIsoForVM] dataType=%s, filename=%s, content=%s", dataType, fileName, (PASSWORD_FILE.equals(fileName) ? "********" : content))); + + createFileInTempDirAnAppendOpenStackMetadataToJsonObject(tempDirName, metaData, dataType, fileName, content); + } + return metaData; + } + + static void createFileInTempDirAnAppendOpenStackMetadataToJsonObject(String tempDirName, JsonObject metaData, String dataType, String fileName, String content) { + if (StringUtils.isBlank(dataType)) { + return; + } + //create folder + File typeFolder = new File(tempDirName + ConfigDrive.cloudStackConfigDriveName + dataType); + if (!typeFolder.exists() && !typeFolder.mkdirs()) { + throw new CloudRuntimeException("Failed to create folder: " + typeFolder); + } + if (StringUtils.isNotBlank(content)) { + File file = new File(typeFolder, fileName + ".txt"); + try { + if (fileName.equals(USERDATA_FILE)) { + // User Data is passed as a base64 encoded string + FileUtils.writeByteArrayToFile(file, Base64.decodeBase64(content)); + } else { + FileUtils.write(file, content, com.cloud.utils.StringUtils.getPreferredCharset()); + } + } catch (IOException ex) { + throw new CloudRuntimeException("Failed to create file ", ex); + } + } + + //now write the file to the OpenStack directory + buildOpenStackMetaData(metaData, dataType, fileName, content); + } + + /** + * Hard link the user_data.txt file with the user_data file in the OpenStack directory. + */ + static void linkUserData(String tempDirName) { String userDataFilePath = tempDirName + ConfigDrive.cloudStackConfigDriveName + "userdata/user_data.txt"; - if ((new File(userDataFilePath).exists())) { + File file = new File(userDataFilePath); + if (file.exists()) { Script hardLink = new Script("ln", Duration.standardSeconds(300), LOG); hardLink.add(userDataFilePath); hardLink.add(tempDirName + ConfigDrive.openStackConfigDriveName + "user_data"); LOG.debug("execute command: " + hardLink.toString()); - return hardLink.execute(); + + String executionResult = hardLink.execute(); + if (StringUtils.isNotBlank(executionResult)) { + throw new CloudRuntimeException("Unable to create user_data link due to " + executionResult); + } } - return null; } private static JsonArray arrayOf(JsonElement... elements) { @@ -193,30 +282,33 @@ public class ConfigDriveBuilder { return array; } - private static JsonObject buildOpenStackMetaData(JsonObject metaData, String dataType, String fileName, String content) { - if (dataType.equals(NetworkModel.METATDATA_DIR) && StringUtils.isNotEmpty(content)) { - //keys are a special case in OpenStack format - if (NetworkModel.PUBLIC_KEYS_FILE.equals(fileName)) { - String[] keyArray = content.replace("\\n", "").split(" "); - String keyName = "key"; - if (keyArray.length > 3 && StringUtils.isNotEmpty(keyArray[2])){ - keyName = keyArray[2]; - } - - JsonObject keyLegacy = new JsonObject(); - keyLegacy.addProperty("type", "ssh"); - keyLegacy.addProperty("data", content.replace("\\n", "")); - keyLegacy.addProperty("name", keyName); - metaData.add("keys", arrayOf(keyLegacy)); - - JsonObject key = new JsonObject(); - key.addProperty(keyName, content); - metaData.add("public_keys", key); - } else if (NetworkModel.openStackFileMapping.get(fileName) != null) { - metaData.addProperty(NetworkModel.openStackFileMapping.get(fileName), content); - } + private static void buildOpenStackMetaData(JsonObject metaData, String dataType, String fileName, String content) { + if (!NetworkModel.METATDATA_DIR.equals(dataType)) { + return; + } + if (StringUtils.isNotBlank(content)) { + return; + } + //keys are a special case in OpenStack format + if (NetworkModel.PUBLIC_KEYS_FILE.equals(fileName)) { + String[] keyArray = content.replace("\\n", "").split(" "); + String keyName = "key"; + if (keyArray.length > 3 && StringUtils.isNotEmpty(keyArray[2])) { + keyName = keyArray[2]; + } + + JsonObject keyLegacy = new JsonObject(); + keyLegacy.addProperty("type", "ssh"); + keyLegacy.addProperty("data", content.replace("\\n", "")); + keyLegacy.addProperty("name", keyName); + metaData.add("keys", arrayOf(keyLegacy)); + + JsonObject key = new JsonObject(); + key.addProperty(keyName, content); + metaData.add("public_keys", key); + } else if (NetworkModel.openStackFileMapping.get(fileName) != null) { + metaData.addProperty(NetworkModel.openStackFileMapping.get(fileName), content); } - return metaData; } } diff --git a/engine/storage/configdrive/src/test/java/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java b/engine/storage/configdrive/src/test/java/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java index 50a4384d5c8..96032738edc 100644 --- a/engine/storage/configdrive/src/test/java/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java +++ b/engine/storage/configdrive/src/test/java/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java @@ -17,49 +17,481 @@ package org.apache.cloudstack.storage.configdrive; +import static org.mockito.Mockito.times; + import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.reflections.ReflectionUtils; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.google.gson.JsonObject; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({FileUtils.class}) public class ConfigDriveBuilderTest { @Test - public void testConfigDriveIsoPath() throws IOException { - Assert.assertEquals(ConfigDrive.createConfigDrivePath("i-x-y"), "configdrive/i-x-y/configdrive.iso"); + public void writeFileTest() throws Exception { + PowerMockito.mockStatic(FileUtils.class); + + ConfigDriveBuilder.writeFile(new File("folder"), "subfolder", "content"); + + PowerMockito.verifyStatic(); + FileUtils.write(Mockito.any(File.class), Mockito.anyString(), Mockito.any(Charset.class), Mockito.eq(false)); + } + + @SuppressWarnings("unchecked") + @Test(expected = CloudRuntimeException.class) + public void writeFileTestwriteFileTestIOExceptionWhileWritingFile() throws Exception { + PowerMockito.mockStatic(FileUtils.class); + + //Does not look good, I know... but this is the price of static methods. + Method method = ReflectionUtils.getMethods(FileUtils.class, ReflectionUtils.withParameters(File.class, CharSequence.class, Charset.class, Boolean.TYPE)).iterator().next(); + PowerMockito.when(FileUtils.class, method).withArguments(Mockito.any(File.class), Mockito.anyString(), Mockito.any(Charset.class), Mockito.anyBoolean()).thenThrow(IOException.class); + + ConfigDriveBuilder.writeFile(new File("folder"), "subfolder", "content"); } @Test - public void testConfigDriveBuild() throws IOException { - List actualVmData = Arrays.asList( - new String[]{"userdata", "user_data", "c29tZSB1c2VyIGRhdGE="}, - new String[]{"metadata", "service-offering", "offering"}, - new String[]{"metadata", "availability-zone", "zone1"}, - new String[]{"metadata", "local-hostname", "hostname"}, - new String[]{"metadata", "local-ipv4", "192.168.111.111"}, - new String[]{"metadata", "public-hostname", "7.7.7.7"}, - new String[]{"metadata", "public-ipv4", "7.7.7.7"}, - new String[]{"metadata", "vm-id", "uuid"}, - new String[]{"metadata", "instance-id", "i-x-y"}, - new String[]{"metadata", "public-keys", "ssh-rsa some-key"}, - new String[]{"metadata", "cloud-identifier", String.format("CloudStack-{%s}", "uuid")}, - new String[]{"password", "vm_password", "password123"} - ); + public void fileToBase64StringTest() throws Exception { + PowerMockito.mockStatic(FileUtils.class); - final Path tempDir = Files.createTempDirectory(ConfigDrive.CONFIGDRIVEDIR); - final String isoData = ConfigDriveBuilder.buildConfigDrive(actualVmData, "i-x-y.iso", "config-2"); - final File isoFile = ConfigDriveBuilder.base64StringToFile(isoData, tempDir.toAbsolutePath().toString(), ConfigDrive.CONFIGDRIVEFILENAME); + String fileContent = "content"; + Method method = getFileUtilsReadfileToByteArrayMethod(); + PowerMockito.when(FileUtils.class, method).withArguments(Mockito.any(File.class)).thenReturn(fileContent.getBytes()); - Assert.assertTrue(isoFile.exists()); - Assert.assertTrue(isoFile.isFile()); - Assert.assertTrue(isoFile.length() > 0L); + String returnedContentInBase64 = ConfigDriveBuilder.fileToBase64String(new File("file")); - FileUtils.deleteDirectory(tempDir.toFile()); + Assert.assertEquals("Y29udGVudA==", returnedContentInBase64); + } + + @SuppressWarnings("unchecked") + @Test(expected = IOException.class) + public void fileToBase64StringTestIOException() throws Exception { + PowerMockito.mockStatic(FileUtils.class); + + Method method = getFileUtilsReadfileToByteArrayMethod(); + PowerMockito.when(FileUtils.class, method).withArguments(Mockito.any(File.class)).thenThrow(IOException.class); + + ConfigDriveBuilder.fileToBase64String(new File("file")); + } + + @SuppressWarnings("unchecked") + private Method getFileUtilsReadfileToByteArrayMethod() { + return ReflectionUtils.getMethods(FileUtils.class, ReflectionUtils.withName("readFileToByteArray")).iterator().next(); + } + + @Test + public void base64StringToFileTest() throws Exception { + String encodedIsoData = "Y29udGVudA=="; + + String parentFolder = "parentFolder"; + String fileName = "fileName"; + + File parentFolderFile = new File(parentFolder); + parentFolderFile.mkdir(); + + ConfigDriveBuilder.base64StringToFile(encodedIsoData, parentFolder, fileName); + + File file = new File(parentFolderFile, fileName); + String contentOfFile = new String(FileUtils.readFileToByteArray(file), StandardCharsets.US_ASCII); + + Assert.assertEquals("content", contentOfFile); + + file.delete(); + parentFolderFile.delete(); + } + + @Test(expected = CloudRuntimeException.class) + public void buildConfigDriveTestNoVmData() { + ConfigDriveBuilder.buildConfigDrive(null, "teste", "C:"); + } + + @SuppressWarnings("unchecked") + @PrepareForTest({ConfigDriveBuilder.class}) + @Test(expected = CloudRuntimeException.class) + public void buildConfigDriveTestIoException() throws Exception { + PowerMockito.mockStatic(ConfigDriveBuilder.class); + + Method method = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("writeVendorAndNetworkEmptyJsonFile")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, method).withArguments(Mockito.any(File.class)).thenThrow(IOException.class); + + //This is odd, but it was necessary to allow us to check if we catch the IOexception and re-throw as a CloudRuntimeException + //We are mocking the class being tested; therefore, we needed to force the execution of the real method we want to test. + PowerMockito.when(ConfigDriveBuilder.class, new ArrayList<>(), "teste", "C:").thenCallRealMethod(); + + ConfigDriveBuilder.buildConfigDrive(new ArrayList<>(), "teste", "C:"); + } + + @Test + @SuppressWarnings("unchecked") + @PrepareForTest({ConfigDriveBuilder.class}) + public void buildConfigDriveTest() throws Exception { + PowerMockito.mockStatic(ConfigDriveBuilder.class); + + Method writeVendorAndNetworkEmptyJsonFileMethod = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("writeVendorAndNetworkEmptyJsonFile")).iterator().next(); + PowerMockito.doNothing().when(ConfigDriveBuilder.class, writeVendorAndNetworkEmptyJsonFileMethod).withArguments(Mockito.any(File.class)); + + Method writeVmMetadataMethod = getWriteVmMetadataMethod(); + PowerMockito.doNothing().when(ConfigDriveBuilder.class, writeVmMetadataMethod).withArguments(Mockito.anyListOf(String[].class), Mockito.anyString(), Mockito.any(File.class)); + + Method linkUserDataMethod = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("linkUserData")).iterator().next(); + PowerMockito.doNothing().when(ConfigDriveBuilder.class, linkUserDataMethod).withArguments(Mockito.anyString()); + + Method generateAndRetrieveIsoAsBase64IsoMethod = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("generateAndRetrieveIsoAsBase64Iso")).iterator().next(); + PowerMockito.doReturn("mockIsoDataBase64").when(ConfigDriveBuilder.class, generateAndRetrieveIsoAsBase64IsoMethod).withArguments(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + + //force execution of real method + PowerMockito.when(ConfigDriveBuilder.class, new ArrayList<>(), "teste", "C:").thenCallRealMethod(); + + String returnedIsoData = ConfigDriveBuilder.buildConfigDrive(new ArrayList<>(), "teste", "C:"); + + Assert.assertEquals("mockIsoDataBase64", returnedIsoData); + + PowerMockito.verifyStatic(); + ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(Mockito.any(File.class)); + ConfigDriveBuilder.writeVmMetadata(Mockito.anyListOf(String[].class), Mockito.anyString(), Mockito.any(File.class)); + ConfigDriveBuilder.linkUserData(Mockito.anyString()); + ConfigDriveBuilder.generateAndRetrieveIsoAsBase64Iso(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + } + + @SuppressWarnings("unchecked") + private Method getWriteVmMetadataMethod() { + return ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("writeVmMetadata")).iterator().next(); + } + + @Test(expected = CloudRuntimeException.class) + public void writeVendorAndNetworkEmptyJsonFileTestCannotCreateOpenStackFolder() { + File folderFileMock = Mockito.mock(File.class); + Mockito.doReturn(false).when(folderFileMock).mkdirs(); + + ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(folderFileMock); + } + + @Test(expected = CloudRuntimeException.class) + public void writeVendorAndNetworkEmptyJsonFileTest() { + File folderFileMock = Mockito.mock(File.class); + Mockito.doReturn(false).when(folderFileMock).mkdirs(); + + ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(folderFileMock); + } + + @Test + @PrepareForTest({ConfigDriveBuilder.class}) + public void writeVendorAndNetworkEmptyJsonFileTestCreatingFolder() throws Exception { + PowerMockito.mockStatic(ConfigDriveBuilder.class); + + File folderFileMock = Mockito.mock(File.class); + Mockito.doReturn(false).when(folderFileMock).exists(); + Mockito.doReturn(true).when(folderFileMock).mkdirs(); + + //force execution of real method + Method writeVendorAndNetworkEmptyJsonFileMethod = getWriteVendorAndNetworkEmptyJsonFileMethod(); + PowerMockito.when(ConfigDriveBuilder.class, writeVendorAndNetworkEmptyJsonFileMethod).withArguments(folderFileMock).thenCallRealMethod(); + + ConfigDriveBuilder.writeVendorAndNetworkEmptyJsonFile(folderFileMock); + + Mockito.verify(folderFileMock).exists(); + Mockito.verify(folderFileMock).mkdirs(); + + PowerMockito.verifyStatic(); + ConfigDriveBuilder.writeFile(Mockito.any(File.class), Mockito.eq("vendor_data.json"), Mockito.eq("{}")); + ConfigDriveBuilder.writeFile(Mockito.any(File.class), Mockito.eq("network_data.json"), Mockito.eq("{}")); + } + + @SuppressWarnings("unchecked") + private Method getWriteVendorAndNetworkEmptyJsonFileMethod() { + return ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("writeVendorAndNetworkEmptyJsonFile")).iterator().next(); + } + + @Test + @SuppressWarnings("unchecked") + @PrepareForTest({ConfigDriveBuilder.class}) + public void writeVmMetadataTest() throws Exception { + PowerMockito.mockStatic(ConfigDriveBuilder.class); + + Method method = getWriteVmMetadataMethod(); + PowerMockito.when(ConfigDriveBuilder.class, method).withArguments(new ArrayList<>(), "metadataFile", new File("folder")).thenCallRealMethod(); + + Method createJsonObjectWithVmDataMethod = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("createJsonObjectWithVmData")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, createJsonObjectWithVmDataMethod).withArguments(Mockito.anyListOf(String[].class), Mockito.any(File.class)).thenReturn(new JsonObject()); + + ConfigDriveBuilder.writeVmMetadata(new ArrayList<>(), "metadataFile", new File("folder")); + + PowerMockito.verifyStatic(); + ConfigDriveBuilder.createJsonObjectWithVmData(Mockito.anyListOf(String[].class), Mockito.anyString()); + ConfigDriveBuilder.writeFile(Mockito.any(File.class), Mockito.eq("meta_data.json"), Mockito.eq("{}")); + } + + @Test + @PrepareForTest({File.class, Script.class, ConfigDriveBuilder.class}) + public void linkUserDataTestUserDataFilePathDoesNotExist() throws Exception { + File fileMock = Mockito.mock(File.class); + Mockito.doReturn(false).when(fileMock).exists(); + + PowerMockito.mockStatic(File.class, Script.class); + PowerMockito.whenNew(File.class).withArguments(Mockito.anyString()).thenReturn(fileMock); + + Script scriptMock = Mockito.mock(Script.class); + PowerMockito.whenNew(Script.class).withAnyArguments().thenReturn(scriptMock); + + ConfigDriveBuilder.linkUserData("test"); + + Mockito.verify(scriptMock, times(0)).execute(); + } + + @Test(expected = CloudRuntimeException.class) + @PrepareForTest({File.class, Script.class, ConfigDriveBuilder.class}) + public void linkUserDataTestUserDataFilePathExistAndExecutionPresentedSomeError() throws Exception { + File fileMock = Mockito.mock(File.class); + Mockito.doReturn(true).when(fileMock).exists(); + + PowerMockito.mockStatic(File.class, Script.class); + PowerMockito.whenNew(File.class).withArguments(Mockito.anyString()).thenReturn(fileMock); + + Script scriptMock = Mockito.mock(Script.class); + PowerMockito.whenNew(Script.class).withAnyArguments().thenReturn(scriptMock); + + Mockito.doReturn("message").when(scriptMock).execute(); + ConfigDriveBuilder.linkUserData("test"); + } + + @Test + @PrepareForTest({File.class, Script.class, ConfigDriveBuilder.class}) + public void linkUserDataTest() throws Exception { + File fileMock = Mockito.mock(File.class); + Mockito.doReturn(true).when(fileMock).exists(); + + PowerMockito.mockStatic(File.class, Script.class); + PowerMockito.whenNew(File.class).withArguments(Mockito.anyString()).thenReturn(fileMock); + + Script scriptMock = Mockito.mock(Script.class); + PowerMockito.whenNew(Script.class).withAnyArguments().thenReturn(scriptMock); + + Mockito.doReturn(StringUtils.EMPTY).when(scriptMock).execute(); + String tempDirName = "test"; + ConfigDriveBuilder.linkUserData(tempDirName); + + Mockito.verify(scriptMock).add(tempDirName + ConfigDrive.cloudStackConfigDriveName + "userdata/user_data.txt"); + Mockito.verify(scriptMock).add(tempDirName + ConfigDrive.openStackConfigDriveName + "user_data"); + Mockito.verify(scriptMock).execute(); + } + + @SuppressWarnings("unchecked") + @Test(expected = CloudRuntimeException.class) + @PrepareForTest({Script.class, ConfigDriveBuilder.class}) + public void generateAndRetrieveIsoAsBase64IsoTestGenIsoFailure() throws Exception { + PowerMockito.mockStatic(Script.class, ConfigDriveBuilder.class); + + Script scriptMock = Mockito.mock(Script.class); + PowerMockito.whenNew(Script.class).withAnyArguments().thenReturn(scriptMock); + + Mockito.doReturn("scriptMessage").when(scriptMock).execute(); + + Method method = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("generateAndRetrieveIsoAsBase64Iso")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, method).withArguments(Mockito.any(File.class), Mockito.any(File.class), Mockito.any(File.class)).thenCallRealMethod(); + + Method getProgramToGenerateIsoMethod = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("getProgramToGenerateIso")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, getProgramToGenerateIsoMethod).withNoArguments().thenReturn("/usr/bin/genisoimage"); + + ConfigDriveBuilder.generateAndRetrieveIsoAsBase64Iso("isoFileName", "driveLabel", "tempDirName"); + } + + @SuppressWarnings("unchecked") + @Test(expected = CloudRuntimeException.class) + @PrepareForTest({File.class, Script.class, ConfigDriveBuilder.class}) + public void generateAndRetrieveIsoAsBase64IsoTestIsoTooBig() throws Exception { + PowerMockito.mockStatic(File.class, Script.class, ConfigDriveBuilder.class); + + File fileMock = Mockito.mock(File.class); + PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(fileMock); + + Script scriptMock = Mockito.mock(Script.class); + PowerMockito.whenNew(Script.class).withAnyArguments().thenReturn(scriptMock); + + Mockito.doReturn(StringUtils.EMPTY).when(scriptMock).execute(); + Mockito.doReturn(64L * 1024L * 1024L + 1l).when(fileMock).length(); + + Method method = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("generateAndRetrieveIsoAsBase64Iso")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, method).withArguments(Mockito.any(File.class), Mockito.any(File.class), Mockito.any(File.class)).thenCallRealMethod(); + + Method getProgramToGenerateIsoMethod = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("getProgramToGenerateIso")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, getProgramToGenerateIsoMethod).withNoArguments().thenReturn("/usr/bin/genisoimage"); + + ConfigDriveBuilder.generateAndRetrieveIsoAsBase64Iso("isoFileName", "driveLabel", "tempDirName"); + } + + @Test + @SuppressWarnings("unchecked") + @PrepareForTest({File.class, Script.class, ConfigDriveBuilder.class}) + public void generateAndRetrieveIsoAsBase64IsoTest() throws Exception { + PowerMockito.mockStatic(File.class, Script.class, ConfigDriveBuilder.class); + + File fileMock = Mockito.mock(File.class); + PowerMockito.whenNew(File.class).withArguments("tempDirName", "isoFileName").thenReturn(fileMock); + + Script scriptMock = Mockito.mock(Script.class); + PowerMockito.whenNew(Script.class).withAnyArguments().thenReturn(scriptMock); + + Mockito.when(fileMock.getAbsolutePath()).thenReturn("absolutePath"); + Mockito.doReturn(StringUtils.EMPTY).when(scriptMock).execute(); + Mockito.doReturn(64L * 1024L * 1024L).when(fileMock).length(); + + Method method = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("generateAndRetrieveIsoAsBase64Iso")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, method).withArguments(Mockito.any(File.class), Mockito.any(File.class), Mockito.any(File.class)).thenCallRealMethod(); + + Method getProgramToGenerateIsoMethod = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("getProgramToGenerateIso")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, getProgramToGenerateIsoMethod).withNoArguments().thenReturn("/usr/bin/genisoimage"); + + ConfigDriveBuilder.generateAndRetrieveIsoAsBase64Iso("isoFileName", "driveLabel", "tempDirName"); + + InOrder inOrder = Mockito.inOrder(scriptMock); + inOrder.verify(scriptMock).add("-o", "absolutePath"); + inOrder.verify(scriptMock).add("-ldots"); + inOrder.verify(scriptMock).add("-allow-lowercase"); + inOrder.verify(scriptMock).add("-allow-multidot"); + inOrder.verify(scriptMock).add("-cache-inodes"); + inOrder.verify(scriptMock).add("-l"); + inOrder.verify(scriptMock).add("-quiet"); + inOrder.verify(scriptMock).add("-J"); + inOrder.verify(scriptMock).add("-r"); + inOrder.verify(scriptMock).add("-V", "driveLabel"); + inOrder.verify(scriptMock).add("tempDirName"); + inOrder.verify(scriptMock).execute(); + + PowerMockito.verifyStatic(); + ConfigDriveBuilder.fileToBase64String(Mockito.any(File.class)); + + } + + @Test + @SuppressWarnings("unchecked") + @PrepareForTest({ConfigDriveBuilder.class}) + public void createJsonObjectWithVmDataTesT() throws Exception { + PowerMockito.mockStatic(ConfigDriveBuilder.class); + + Method method = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("createJsonObjectWithVmData")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, method).withArguments(Mockito.anyListOf(String[].class), Mockito.anyString()).thenCallRealMethod(); + + List vmData = new ArrayList<>(); + vmData.add(new String[] {"dataType", "fileName", "content"}); + vmData.add(new String[] {"dataType2", "fileName2", "content2"}); + + ConfigDriveBuilder.createJsonObjectWithVmData(vmData, "tempDirName"); + + PowerMockito.verifyStatic(Mockito.times(1)); + ConfigDriveBuilder.createFileInTempDirAnAppendOpenStackMetadataToJsonObject(Mockito.eq("tempDirName"), Mockito.any(JsonObject.class), Mockito.eq("dataType"), Mockito.eq("fileName"), + Mockito.eq("content")); + ConfigDriveBuilder.createFileInTempDirAnAppendOpenStackMetadataToJsonObject(Mockito.eq("tempDirName"), Mockito.any(JsonObject.class), Mockito.eq("dataType2"), Mockito.eq("fileName2"), + Mockito.eq("content2")); + } + + @Test + @PrepareForTest({File.class, ConfigDriveBuilder.class}) + public void getProgramToGenerateIsoTestGenIsoExistsAndIsExecutable() throws Exception { + PowerMockito.mockStatic(File.class); + + File genIsoFileMock = Mockito.mock(File.class); + Mockito.doReturn(true).when(genIsoFileMock).exists(); + Mockito.doReturn(true).when(genIsoFileMock).canExecute(); + + PowerMockito.whenNew(File.class).withArguments("/usr/bin/genisoimage").thenReturn(genIsoFileMock); + + ConfigDriveBuilder.getProgramToGenerateIso(); + + Mockito.verify(genIsoFileMock, Mockito.times(2)).exists(); + Mockito.verify(genIsoFileMock).canExecute(); + Mockito.verify(genIsoFileMock).getCanonicalPath(); + } + + @Test(expected = CloudRuntimeException.class) + @PrepareForTest({File.class, ConfigDriveBuilder.class}) + public void getProgramToGenerateIsoTestGenIsoExistsbutNotExecutable() throws Exception { + PowerMockito.mockStatic(File.class); + + File genIsoFileMock = Mockito.mock(File.class); + Mockito.doReturn(true).when(genIsoFileMock).exists(); + Mockito.doReturn(false).when(genIsoFileMock).canExecute(); + + PowerMockito.whenNew(File.class).withArguments("/usr/bin/genisoimage").thenReturn(genIsoFileMock); + + ConfigDriveBuilder.getProgramToGenerateIso(); + } + + @Test + @PrepareForTest({File.class, ConfigDriveBuilder.class}) + public void getProgramToGenerateIsoTestNotGenIsoMkIsoInLinux() throws Exception { + PowerMockito.mockStatic(File.class); + + File genIsoFileMock = Mockito.mock(File.class); + Mockito.doReturn(false).when(genIsoFileMock).exists(); + + File mkIsoProgramInLinuxFileMock = Mockito.mock(File.class); + Mockito.doReturn(true).when(mkIsoProgramInLinuxFileMock).exists(); + Mockito.doReturn(true).when(mkIsoProgramInLinuxFileMock).canExecute(); + + PowerMockito.whenNew(File.class).withArguments("/usr/bin/genisoimage").thenReturn(genIsoFileMock); + PowerMockito.whenNew(File.class).withArguments("/usr/bin/mkisofs").thenReturn(mkIsoProgramInLinuxFileMock); + + ConfigDriveBuilder.getProgramToGenerateIso(); + + Mockito.verify(genIsoFileMock, Mockito.times(1)).exists(); + Mockito.verify(genIsoFileMock, Mockito.times(0)).canExecute(); + Mockito.verify(genIsoFileMock, Mockito.times(0)).getCanonicalPath(); + + Mockito.verify(mkIsoProgramInLinuxFileMock, Mockito.times(2)).exists(); + Mockito.verify(mkIsoProgramInLinuxFileMock, Mockito.times(1)).canExecute(); + Mockito.verify(mkIsoProgramInLinuxFileMock, Mockito.times(1)).getCanonicalPath(); + } + + @Test + @PrepareForTest({File.class, ConfigDriveBuilder.class}) + public void getProgramToGenerateIsoTestMkIsoMac() throws Exception { + PowerMockito.mockStatic(File.class); + + File genIsoFileMock = Mockito.mock(File.class); + Mockito.doReturn(false).when(genIsoFileMock).exists(); + + File mkIsoProgramInLinuxFileMock = Mockito.mock(File.class); + Mockito.doReturn(false).when(mkIsoProgramInLinuxFileMock).exists(); + + File mkIsoProgramInMacOsFileMock = Mockito.mock(File.class); + Mockito.doReturn(true).when(mkIsoProgramInMacOsFileMock).exists(); + Mockito.doReturn(true).when(mkIsoProgramInMacOsFileMock).canExecute(); + + PowerMockito.whenNew(File.class).withArguments("/usr/bin/genisoimage").thenReturn(genIsoFileMock); + PowerMockito.whenNew(File.class).withArguments("/usr/bin/mkisofs").thenReturn(mkIsoProgramInLinuxFileMock); + PowerMockito.whenNew(File.class).withArguments("/usr/local/bin/mkisofs").thenReturn(mkIsoProgramInMacOsFileMock); + + ConfigDriveBuilder.getProgramToGenerateIso(); + + Mockito.verify(genIsoFileMock, Mockito.times(1)).exists(); + Mockito.verify(genIsoFileMock, Mockito.times(0)).canExecute(); + Mockito.verify(genIsoFileMock, Mockito.times(0)).getCanonicalPath(); + + Mockito.verify(mkIsoProgramInLinuxFileMock, Mockito.times(1)).exists(); + Mockito.verify(mkIsoProgramInLinuxFileMock, Mockito.times(0)).canExecute(); + Mockito.verify(mkIsoProgramInLinuxFileMock, Mockito.times(0)).getCanonicalPath(); + + Mockito.verify(mkIsoProgramInMacOsFileMock, Mockito.times(1)).exists(); + Mockito.verify(mkIsoProgramInMacOsFileMock, Mockito.times(1)).canExecute(); + Mockito.verify(mkIsoProgramInMacOsFileMock, Mockito.times(1)).getCanonicalPath(); } } \ No newline at end of file diff --git a/engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveTest.java b/engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveTest.java new file mode 100644 index 00000000000..81294d4331b --- /dev/null +++ b/engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveTest.java @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.storage.configdrive; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + +public class ConfigDriveTest { + + @Test + public void testConfigDriveIsoPath() throws IOException { + Assert.assertEquals(ConfigDrive.createConfigDrivePath("i-x-y"), "configdrive/i-x-y/configdrive.iso"); + } + +} \ No newline at end of file diff --git a/plugins/hypervisors/hyperv/src/main/java/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/main/java/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java index bc955c809b1..1f9ad02b61f 100644 --- a/plugins/hypervisors/hyperv/src/main/java/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java +++ b/plugins/hypervisors/hyperv/src/main/java/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java @@ -1375,7 +1375,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S if (!result.first()) { s_logger.error("Unable to copy haproxy configuration file"); - return new Answer(cmd, false, "LoadBalancerConfigCommand failed due to uanble to copy haproxy configuration file"); + return new Answer(cmd, false, "LoadBalancerConfigCommand failed due to unable to copy haproxy configuration file"); } final String command = String.format("%s%s %s", "/root/", VRScripts.LB, args); diff --git a/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/element/NuageVspElement.java b/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/element/NuageVspElement.java index d5496b0157a..48c3d88d0d0 100644 --- a/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/element/NuageVspElement.java +++ b/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/element/NuageVspElement.java @@ -54,6 +54,7 @@ import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupVspCommand; import com.cloud.agent.api.element.ApplyAclRuleVspCommand; @@ -289,14 +290,41 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider VspDhcpDomainOption vspDhcpOptions = _nuageVspEntityBuilder.buildNetworkDhcpOption(network, offering); HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(network.getPhysicalNetworkId()); ImplementVspCommand cmd = new ImplementVspCommand(vspNetwork, ingressFirewallRules, egressFirewallRules, floatingIpUuids, vspDhcpOptions); + send(cmd, network); + + return true; + } + + private void send(Command cmd, Network network) + throws ResourceUnavailableException { + send(cmd, network.getPhysicalNetworkId(), Network.class, network); + } + + private void send(Command cmd, Vpc vpc) + throws ResourceUnavailableException { + send(cmd, getPhysicalNetworkId(vpc.getZoneId()), Vpc.class, vpc); + } + + + private void send(Command cmd, long physicalNetworkId, Class resourceClass, + R resource) + throws ResourceUnavailableException { + HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(physicalNetworkId); Answer answer = _agentMgr.easySend(nuageVspHost.getId(), cmd); - if (answer == null || !answer.getResult()) { - s_logger.error("ImplementVspCommand for network " + network.getUuid() + " failed on Nuage VSD " + nuageVspHost.getDetail("hostname")); - if ((null != answer) && (null != answer.getDetails())) { - throw new ResourceUnavailableException(answer.getDetails(), Network.class, network.getId()); + if (isFailure(answer)) { + s_logger.error(cmd.getClass().getName() + " for " + resourceClass.getName() + " " + resource.getId() + " failed on Nuage VSD " + nuageVspHost.getDetail("hostname")); + if (hasFailureDetails(answer)) { + throw new ResourceUnavailableException(answer.getDetails(), resourceClass, resource.getId()); } } - return true; + } + + private boolean hasFailureDetails(Answer answer) { + return (null != answer) && (null != answer.getDetails()); + } + + private boolean isFailure(Answer answer) { + return answer == null || !answer.getResult(); } private boolean applyACLRulesForVpc(Network network, NetworkOffering offering) throws ResourceUnavailableException { @@ -358,15 +386,9 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider NetworkOfferingVO networkOfferingVO = _ntwkOfferingDao.findById(network.getNetworkOfferingId()); VspDhcpDomainOption vspDhcpOptions = _nuageVspEntityBuilder.buildNetworkDhcpOption(network, networkOfferingVO); VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network); - HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(network.getPhysicalNetworkId()); + ShutDownVspCommand cmd = new ShutDownVspCommand(vspNetwork, vspDhcpOptions); - Answer answer = _agentMgr.easySend(nuageVspHost.getId(), cmd); - if (answer == null || !answer.getResult()) { - s_logger.error("ShutDownVspCommand for network " + network.getUuid() + " failed on Nuage VSD " + nuageVspHost.getDetail("hostname")); - if ((null != answer) && (null != answer.getDetails())) { - throw new ResourceUnavailableException(answer.getDetails(), Network.class, network.getId()); - } - } + send(cmd, network); } return true; } @@ -501,14 +523,17 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider @Override public boolean setExtraDhcpOptions(Network network, long nicId, Map dhcpOptions) { + if (network.isRollingRestart()) { + return true; + } + VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(network); HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(network.getPhysicalNetworkId()); NicVO nic = _nicDao.findById(nicId); ExtraDhcpOptionsVspCommand extraDhcpOptionsVspCommand = new ExtraDhcpOptionsVspCommand(vspNetwork, nic.getUuid(), dhcpOptions); Answer answer = _agentMgr.easySend(nuageVspHost.getId(), extraDhcpOptionsVspCommand); - - if (answer == null || !answer.getResult()) { + if (isFailure(answer)) { s_logger.error("[setExtraDhcpOptions] setting extra DHCP options for nic " + nic.getUuid() + " failed."); return false; } @@ -539,15 +564,9 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider } VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(config); - HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(config.getPhysicalNetworkId()); ApplyStaticNatVspCommand cmd = new ApplyStaticNatVspCommand(vspNetwork, vspStaticNatDetails); - Answer answer = _agentMgr.easySend(nuageVspHost.getId(), cmd); - if (answer == null || !answer.getResult()) { - s_logger.error("ApplyStaticNatNuageVspCommand for network " + config.getUuid() + " failed on Nuage VSD " + nuageVspHost.getDetail("hostname")); - if ((null != answer) && (null != answer.getDetails())) { - throw new ResourceUnavailableException(answer.getDetails(), Network.class, config.getId()); - } - } + send(cmd, + config); return true; } @@ -611,16 +630,10 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider } }); - HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(network.getPhysicalNetworkId()); VspAclRule.ACLType vspAclType = isNetworkAcl ? VspAclRule.ACLType.NetworkACL : VspAclRule.ACLType.Firewall; ApplyAclRuleVspCommand cmd = new ApplyAclRuleVspCommand(vspAclType, vspNetwork, vspAclRules, networkReset); - Answer answer = _agentMgr.easySend(nuageVspHost.getId(), cmd); - if (answer == null || !answer.getResult()) { - s_logger.error("ApplyAclRuleNuageVspCommand for network " + network.getUuid() + " failed on Nuage VSD " + nuageVspHost.getDetail("hostname")); - if ((null != answer) && (null != answer.getDetails())) { - throw new ResourceUnavailableException(answer.getDetails(), Network.class, network.getId()); - } - } + send(cmd, + network); return true; } @@ -686,7 +699,6 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider }); Domain vpcDomain = _domainDao.findById(vpc.getDomainId()); - HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(getPhysicalNetworkId(vpc.getZoneId())); String preConfiguredDomainTemplateName; VpcDetailVO domainTemplateNameDetail = _vpcDetailsDao.findDetail(vpc.getId(), NuageVspManager.nuageDomainTemplateDetailName); @@ -710,14 +722,7 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider } ShutDownVpcVspCommand cmd = new ShutDownVpcVspCommand(vpcDomain.getUuid(), vpc.getUuid(), preConfiguredDomainTemplateName, domainRouterUuids, vpcDetails); - Answer answer = _agentMgr.easySend(nuageVspHost.getId(), cmd); - if (answer == null || !answer.getResult()) { - s_logger.error("ShutDownVpcVspCommand for VPC " + vpc.getUuid() + " failed on Nuage VSD " + nuageVspHost.getDetail("hostname")); - if ((null != answer) && (null != answer.getDetails())) { - throw new ResourceUnavailableException(answer.getDetails(), Vpc.class, vpc.getId()); - } - return false; - } + send(cmd, vpc); } return true; } diff --git a/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/guru/NuageVspGuestNetworkGuru.java b/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/guru/NuageVspGuestNetworkGuru.java index 9ecefe49087..8d53f0f4268 100644 --- a/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/guru/NuageVspGuestNetworkGuru.java +++ b/plugins/network-elements/nuage-vsp/src/main/java/com/cloud/network/guru/NuageVspGuestNetworkGuru.java @@ -82,6 +82,7 @@ import com.cloud.network.dao.NetworkDetailsDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.manager.NuageVspManager; +import com.cloud.network.router.VirtualRouter; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; @@ -94,6 +95,7 @@ import com.cloud.utils.StringUtils; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Ip; +import com.cloud.vm.DomainRouterVO; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; @@ -101,6 +103,7 @@ import com.cloud.vm.ReservationContext; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.VMInstanceDao; public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements NetworkGuruAdditionalFunctions { @@ -134,6 +137,8 @@ public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements Networ DataCenterDetailsDao _dcDetailsDao; @Inject VlanDetailsDao _vlanDetailsDao; + @Inject + private DomainRouterDao _routerDao; public NuageVspGuestNetworkGuru() { super(); @@ -528,29 +533,34 @@ public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements Networ nic.setBroadcastUri(network.getBroadcastUri()); nic.setIsolationUri(network.getBroadcastUri()); - //NicProfile does not contain the NIC UUID. We need this information to set it in the VMInterface and VPort - //that we create in VSP - NicVO nicFromDb = _nicDao.findById(nic.getId()); - IPAddressVO staticNatIp = _ipAddressDao.findByVmIdAndNetworkId(network.getId(), vm.getId()); VspVm vspVm = _nuageVspEntityBuilder.buildVspVm(vm.getVirtualMachine(), network); - VspNic vspNic = _nuageVspEntityBuilder.buildVspNic(nicFromDb.getUuid(), nic); - VspStaticNat vspStaticNat = null; - if (staticNatIp != null) { - VlanVO staticNatVlan = _vlanDao.findById(staticNatIp.getVlanId()); - vspStaticNat = _nuageVspEntityBuilder.buildVspStaticNat(null, staticNatIp, staticNatVlan, vspNic); - } - boolean defaultHasDns = getDefaultHasDns(networkHasDnsCache, nicFromDb); - VspDhcpVMOption dhcpOption = _nuageVspEntityBuilder.buildVmDhcpOption(nicFromDb, defaultHasDns, networkHasDns); - ReserveVmInterfaceVspCommand cmd = new ReserveVmInterfaceVspCommand(vspNetwork, vspVm, vspNic, vspStaticNat, dhcpOption); - Answer answer = _agentMgr.easySend(nuageVspHost.getId(), cmd); - - if (answer == null || !answer.getResult()) { - s_logger.error("ReserveVmInterfaceNuageVspCommand failed for NIC " + nic.getId() + " attached to VM " + vm.getId() + " in network " + network.getId()); - if ((null != answer) && (null != answer.getDetails())) { - s_logger.error(answer.getDetails()); + if (vm.isRollingRestart()) { + ((NetworkVO)network).setRollingRestart(true); + } else { + //NicProfile does not contain the NIC UUID. We need this information to set it in the VMInterface and VPort + //that we create in VSP + NicVO nicFromDb = _nicDao.findById(nic.getId()); + IPAddressVO staticNatIp = _ipAddressDao.findByVmIdAndNetworkId(network.getId(), vm.getId()); + VspNic vspNic = _nuageVspEntityBuilder.buildVspNic(nicFromDb.getUuid(), nic); + VspStaticNat vspStaticNat = null; + if (staticNatIp != null) { + VlanVO staticNatVlan = _vlanDao.findById(staticNatIp.getVlanId()); + vspStaticNat = _nuageVspEntityBuilder.buildVspStaticNat(null, staticNatIp, staticNatVlan, vspNic); + } + + boolean defaultHasDns = getDefaultHasDns(networkHasDnsCache, nicFromDb); + VspDhcpVMOption dhcpOption = _nuageVspEntityBuilder.buildVmDhcpOption(nicFromDb, defaultHasDns, networkHasDns); + ReserveVmInterfaceVspCommand cmd = new ReserveVmInterfaceVspCommand(vspNetwork, vspVm, vspNic, vspStaticNat, dhcpOption); + Answer answer = _agentMgr.easySend(nuageVspHost.getId(), cmd); + + if (answer == null || !answer.getResult()) { + s_logger.error("ReserveVmInterfaceNuageVspCommand failed for NIC " + nic.getId() + " attached to VM " + vm.getId() + " in network " + network.getId()); + if ((null != answer) && (null != answer.getDetails())) { + s_logger.error(answer.getDetails()); + } + throw new InsufficientVirtualNetworkCapacityException("Failed to reserve VM in Nuage VSP.", Network.class, network.getId()); } - throw new InsufficientVirtualNetworkCapacityException("Failed to reserve VM in Nuage VSP.", Network.class, network.getId()); } if (vspVm.getDomainRouter() == Boolean.TRUE) { @@ -695,15 +705,18 @@ public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements Networ } try { + final VirtualMachine virtualMachine = vm.getVirtualMachine(); if (s_logger.isDebugEnabled()) { s_logger.debug("Handling deallocate() call back, which is called when a VM is destroyed or interface is removed, " + "to delete VM Interface with IP " - + nic.getIPv4Address() + " from a VM " + vm.getInstanceName() + " with state " + vm.getVirtualMachine().getState()); + + nic.getIPv4Address() + " from a VM " + vm.getInstanceName() + " with state " + virtualMachine + .getState()); } NicVO nicFromDb = _nicDao.findById(nic.getId()); - VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(vm.getVirtualMachine().getDomainId(), network); - VspVm vspVm = _nuageVspEntityBuilder.buildVspVm(vm.getVirtualMachine(), network); + VspNetwork vspNetwork = _nuageVspEntityBuilder.buildVspNetwork(virtualMachine + .getDomainId(), network); + VspVm vspVm = _nuageVspEntityBuilder.buildVspVm(virtualMachine, network); VspNic vspNic = _nuageVspEntityBuilder.buildVspNic(nicFromDb.getUuid(), nic); HostVO nuageVspHost = _nuageVspManager.getNuageVspHost(network.getPhysicalNetworkId()); @@ -723,6 +736,32 @@ public class NuageVspGuestNetworkGuru extends GuestNetworkGuru implements Networ } else { super.deallocate(network, nic, vm); } + + if (virtualMachine.getType() == VirtualMachine.Type.DomainRouter) { + final List routers = _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER); + final DomainRouterVO otherRouter = routers.stream() + .filter(r -> r.getId() != vm.getId()) + .findFirst() + .orElse(null); + + if (otherRouter != null) { + nicFromDb = _nicDao.findByNtwkIdAndInstanceId(network.getId(), otherRouter.getId()); + vspVm = _nuageVspEntityBuilder.buildVspVm(otherRouter, network); + vspNic = _nuageVspEntityBuilder.buildVspNic(nicFromDb); + + VspDhcpVMOption dhcpOption = _nuageVspEntityBuilder.buildVmDhcpOption(nicFromDb, false, false); + ReserveVmInterfaceVspCommand reserveCmd = new ReserveVmInterfaceVspCommand(vspNetwork, vspVm, vspNic, null, dhcpOption); + + answer = _agentMgr.easySend(nuageVspHost.getId(), reserveCmd); + if (answer == null || !answer.getResult()) { + s_logger.error("DeallocateVmNuageVspCommand for VM " + vm.getUuid() + " failed on Nuage VSD " + nuageVspHost.getDetail("hostname")); + if ((null != answer) && (null != answer.getDetails())) { + s_logger.error(answer.getDetails()); + } + } + } + + } } finally { if (network != null && lockedNetwork) { _networkDao.releaseFromLockTable(network.getId()); diff --git a/plugins/network-elements/nuage-vsp/src/test/java/com/cloud/network/guru/NuageVspGuestNetworkGuruTest.java b/plugins/network-elements/nuage-vsp/src/test/java/com/cloud/network/guru/NuageVspGuestNetworkGuruTest.java index b1d6771a36a..0708202c215 100644 --- a/plugins/network-elements/nuage-vsp/src/test/java/com/cloud/network/guru/NuageVspGuestNetworkGuruTest.java +++ b/plugins/network-elements/nuage-vsp/src/test/java/com/cloud/network/guru/NuageVspGuestNetworkGuruTest.java @@ -19,15 +19,34 @@ package com.cloud.network.guru; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.List; + +import javax.annotation.Nonnull; import net.nuage.vsp.acs.client.api.model.NetworkRelatedVsdIds; + import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.internal.util.collections.Sets; @@ -39,10 +58,11 @@ import com.cloud.NuageTest; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; +import com.cloud.agent.api.guru.DeallocateVmVspCommand; import com.cloud.agent.api.guru.ImplementNetworkVspCommand; +import com.cloud.agent.api.guru.ReserveVmInterfaceVspCommand; import com.cloud.agent.api.manager.ImplementNetworkVspAnswer; import com.cloud.configuration.ConfigurationManager; -import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; @@ -71,6 +91,7 @@ import com.cloud.network.dao.NuageVspDao; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.manager.NuageVspManager; +import com.cloud.network.router.VirtualRouter; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; @@ -78,26 +99,34 @@ import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; +import com.cloud.vm.DomainRouterVO; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class NuageVspGuestNetworkGuruTest extends NuageTest { + private static final long DATACENTER_ID = 100L; + private static final long HOST_ID = 101L; + private static final long DOMAIN_ID = 1L; + private static final long ACCOUNT_ID = 2L; + private static final long OFFERING_ID = 40L; private static final long NETWORK_ID = 42L; + private static final long VM_ID = 242L; + private static final long NIC_ID = 342L; + + private static final String DATACENTER_UUID = "uuid-datacenter-100"; + private static final String HOST_UUID = "uuid-host-101"; + private static final String DOMAIN_UUID = "uuid-domain-001"; + private static final String ACCOUNT_UUID = "uuid-account-002"; + private static final String OFFERING_UUID = "uuid-offering-040"; + private static final String NETWORK_UUID = "uuid-network-000-42"; + private static final String VM_UUID = "uuid-vm-002-42"; + private static final String NIC_UUID = "uuid-nic-003-42"; + @Mock private PhysicalNetworkDao _physicalNetworkDao; @Mock private DataCenterDao _dataCenterDao; @Mock private NetworkOfferingServiceMapDao _networkOfferingServiceMapDao; @@ -115,39 +144,42 @@ public class NuageVspGuestNetworkGuruTest extends NuageTest { @Mock private DataCenterDetailsDao _dcDetailsDao; @Mock private NetworkDetailsDao _networkDetailsDao; @Mock private PhysicalNetworkVO physnet; + @Mock private DomainRouterDao _routerDao; + + private Account _account; + private Domain _domain; + private DataCenterVO _dc; + private ReservationContext _reservationContext; @InjectMocks - private NuageVspGuestNetworkGuru _nuageVspGuestNetworkGuru; + private NuageVspGuestNetworkGuru _nuageVspGuestNetworkGuru = new NuageVspGuestNetworkGuru(); @Before public void setUp() throws Exception { - _nuageVspGuestNetworkGuru = new NuageVspGuestNetworkGuru(); - super.setUp(); - _nuageVspGuestNetworkGuru._nuageVspEntityBuilder = _nuageVspEntityBuilder; - - final DataCenterVO dc = mock(DataCenterVO.class); - when(dc.getNetworkType()).thenReturn(NetworkType.Advanced); - when(dc.getGuestNetworkCidr()).thenReturn("10.1.1.1/24"); - - when(_dataCenterDao.findById((Long)any())).thenReturn(dc); + _account = getMockAccount(); + _domain = getMockDomain(); + _dc = mockDataCenter(); + _reservationContext = getMockReservationContext(_account, _domain); when(_physicalNetworkDao.findById(any(Long.class))).thenReturn(physnet); - when(physnet.getIsolationMethods()).thenReturn(Arrays.asList("VSP")); + when(physnet.getIsolationMethods()).thenReturn(Collections.singletonList("VSP")); when(physnet.getId()).thenReturn(NETWORK_ID); final HostVO host = mock(HostVO.class); - when(_hostDao.findById(NETWORK_ID)).thenReturn(host); - when(host.getId()).thenReturn(NETWORK_ID); - when(_agentManager.easySend(eq(NETWORK_ID), any(Command.class))).thenReturn(new Answer(null)); - when(_agentManager.easySend(eq(NETWORK_ID), any(ImplementNetworkVspCommand.class))).thenAnswer(this::mockImplement); + when(host.getId()).thenReturn(HOST_ID); + when(host.getUuid()).thenReturn(HOST_UUID); + when(_hostDao.findById(HOST_ID)).thenReturn(host); + + when(_agentManager.easySend(eq(HOST_ID), any(Command.class))).thenReturn(new Answer(null)); + when(_agentManager.easySend(eq(HOST_ID), any(ImplementNetworkVspCommand.class))).thenAnswer(this::mockImplement); when(_nuageVspManager.getNuageVspHost(NETWORK_ID)).thenReturn(host); final NuageVspDeviceVO device = mock(NuageVspDeviceVO.class); - when(_nuageVspDao.listByPhysicalNetwork(NETWORK_ID)).thenReturn(Arrays.asList(device)); + when(_nuageVspDao.listByPhysicalNetwork(NETWORK_ID)).thenReturn(Collections.singletonList(device)); when(device.getId()).thenReturn(1L); - when(device.getHostId()).thenReturn(NETWORK_ID); + when(device.getHostId()).thenReturn(HOST_ID); } Answer mockImplement(InvocationOnMock invocation) { @@ -161,18 +193,7 @@ public class NuageVspGuestNetworkGuruTest extends NuageTest { @Test public void testCanHandle() { - final NetworkOffering offering = mock(NetworkOffering.class); - when(offering.getId()).thenReturn(NETWORK_ID); - when(offering.getIsPersistent()).thenReturn(false); - when(_configurationManager.isOfferingForVpc(any(NetworkOffering.class))).thenReturn(false); - - when(_networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(NETWORK_ID, Service.Connectivity, Network.Provider.NuageVsp)).thenReturn(true); - when(_networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(NETWORK_ID, Service.SourceNat, Network.Provider.NuageVsp)).thenReturn(true); - - when(_networkModel.getNetworkOfferingServiceProvidersMap(NETWORK_ID)).thenReturn(ImmutableMap.of( - Service.Connectivity, Sets.newSet(Network.Provider.NuageVsp), - Service.SourceNat, Sets.newSet(Network.Provider.NuageVsp) - )); + final NetworkOffering offering = mockNetworkOffering(false); when(offering.getTrafficType()).thenReturn(TrafficType.Guest); when(offering.getGuestType()).thenReturn(GuestType.Isolated); @@ -191,8 +212,8 @@ public class NuageVspGuestNetworkGuruTest extends NuageTest { when(offering.getGuestType()).thenReturn(GuestType.Isolated); assertThat(_nuageVspGuestNetworkGuru.canHandle(offering, NetworkType.Basic, physnet), is(false)); - // Not supported: IsolationMethod != STT - when(physnet.getIsolationMethods()).thenReturn(Arrays.asList("VLAN")); + // Not supported: IsolationMethod != VSP + when(physnet.getIsolationMethods()).thenReturn(Collections.singletonList("VLAN")); assertThat(_nuageVspGuestNetworkGuru.canHandle(offering, NetworkType.Basic, physnet), is(false)); // Not supported: Non-persistent VPC tier @@ -202,272 +223,289 @@ public class NuageVspGuestNetworkGuruTest extends NuageTest { @Test public void testDesign() { - final NuageVspDeviceVO device = mock(NuageVspDeviceVO.class); - when(_nuageVspDao.listByPhysicalNetwork(NETWORK_ID)).thenReturn(Arrays.asList(device)); - when(device.getId()).thenReturn(1L); - - final NetworkOffering offering = mock(NetworkOffering.class); - when(offering.getId()).thenReturn(NETWORK_ID); - when(offering.getTrafficType()).thenReturn(TrafficType.Guest); - when(offering.getGuestType()).thenReturn(GuestType.Isolated); + final NetworkOffering offering = mockNetworkOffering(false); when(offering.getIsPersistent()).thenReturn(false); - when(_configurationManager.isOfferingForVpc(any(NetworkOffering.class))).thenReturn(false); - when(_networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(NETWORK_ID, Service.Connectivity, Network.Provider.NuageVsp)).thenReturn(true); - when(_networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(NETWORK_ID, Service.SourceNat, Network.Provider.NuageVsp)).thenReturn(true); - - when(_networkModel.getNetworkOfferingServiceProvidersMap(NETWORK_ID)).thenReturn(ImmutableMap.of( - Service.Connectivity, Sets.newSet(Network.Provider.NuageVsp), - Service.SourceNat, Sets.newSet(Network.Provider.NuageVsp) - )); - - final DeploymentPlan plan = mock(DeploymentPlan.class); + final DeploymentPlan plan = mockDeploymentPlan(); final Network network = mock(Network.class); - final Account account = mock(Account.class); - final Network designednetwork = _nuageVspGuestNetworkGuru.design(offering, plan, network, account); + final Network designednetwork = _nuageVspGuestNetworkGuru.design(offering, plan, network, _account); assertThat(designednetwork, notNullValue(Network.class)); assertThat(designednetwork.getBroadcastDomainType(), is(BroadcastDomainType.Vsp)); // Can't design non-persistent VPC tier when(_configurationManager.isOfferingForVpc(any(NetworkOffering.class))).thenReturn(true); - assertThat(_nuageVspGuestNetworkGuru.design(offering, plan, network, account), nullValue(Network.class)); - } - - @Test - public void testDesignNoElementOnPhysicalNetwork() { - when(physnet.getIsolationMethods()).thenReturn(Arrays.asList("STT")); - when(_nuageVspDao.listByPhysicalNetwork(NETWORK_ID)).thenReturn(Collections.emptyList()); - - final NetworkOffering offering = mock(NetworkOffering.class); - when(offering.getId()).thenReturn(NETWORK_ID); - when(offering.getTrafficType()).thenReturn(TrafficType.Guest); - when(offering.getGuestType()).thenReturn(GuestType.Isolated); - - final DeploymentPlan plan = mock(DeploymentPlan.class); - final Network network = mock(Network.class); - final Account account = mock(Account.class); - - final Network designednetwork = _nuageVspGuestNetworkGuru.design(offering, plan, network, account); - assertTrue(designednetwork == null); + assertThat(_nuageVspGuestNetworkGuru.design(offering, plan, network, _account), nullValue(Network.class)); } @Test public void testDesignNoIsolationMethodVSP() { - when(physnet.getIsolationMethods()).thenReturn(Arrays.asList("VLAN")); - when(_nuageVspDao.listByPhysicalNetwork(NETWORK_ID)).thenReturn(Collections.emptyList()); + when(physnet.getIsolationMethods()).thenReturn(Collections.singletonList("VLAN")); - final NetworkOffering offering = mock(NetworkOffering.class); - when(offering.getId()).thenReturn(NETWORK_ID); - when(offering.getTrafficType()).thenReturn(TrafficType.Guest); - when(offering.getGuestType()).thenReturn(GuestType.Isolated); + final NetworkOffering offering = mockNetworkOffering(false); - final DeploymentPlan plan = mock(DeploymentPlan.class); + final DeploymentPlan plan = mockDeploymentPlan(); final Network network = mock(Network.class); - final Account account = mock(Account.class); - final Network designednetwork = _nuageVspGuestNetworkGuru.design(offering, plan, network, account); - assertTrue(designednetwork == null); + assertThat(_nuageVspGuestNetworkGuru.design(offering, plan, network, _account), nullValue(Network.class)); } @Test public void testReserve() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, URISyntaxException { - final NetworkVO network = mock(NetworkVO.class); - when(network.getId()).thenReturn(NETWORK_ID); - when(network.getUuid()).thenReturn("aaaaaa"); - when(network.getDataCenterId()).thenReturn(NETWORK_ID); - when(network.getNetworkOfferingId()).thenReturn(NETWORK_ID); - when(network.getPhysicalNetworkId()).thenReturn(NETWORK_ID); - when(network.getDomainId()).thenReturn(NETWORK_ID); - when(network.getAccountId()).thenReturn(NETWORK_ID); - when(network.getVpcId()).thenReturn(null); - when(network.getBroadcastUri()).thenReturn(new URI("vsp://aaaaaa-aavvv/10.1.1.1")); - - final DataCenterVO dataCenter = mock(DataCenterVO.class); - when(_dataCenterDao.findById(NETWORK_ID)).thenReturn(dataCenter); - final AccountVO networksAccount = mock(AccountVO.class); - when(networksAccount.getId()).thenReturn(NETWORK_ID); - when(networksAccount.getUuid()).thenReturn("aaaa-abbbb"); - when(networksAccount.getType()).thenReturn(Account.ACCOUNT_TYPE_NORMAL); - when(_accountDao.findById(NETWORK_ID)).thenReturn(networksAccount); - final DomainVO networksDomain = mock(DomainVO.class); - when(networksDomain.getId()).thenReturn(NETWORK_ID); - when(networksDomain.getUuid()).thenReturn("aaaaa-bbbbb"); - when(_domainDao.findById(NETWORK_ID)).thenReturn(networksDomain); - - final NicVO nicvo = mock(NicVO.class); - when(nicvo.getId()).thenReturn(NETWORK_ID); - when(nicvo.getMacAddress()).thenReturn("aa-aa-aa-aa-aa-aa"); - when(nicvo.getUuid()).thenReturn("aaaa-fffff"); - when(nicvo.getNetworkId()).thenReturn(NETWORK_ID); - when(nicvo.getInstanceId()).thenReturn(NETWORK_ID); - when(_nicDao.findById(NETWORK_ID)).thenReturn(nicvo); - when(_nicDao.findDefaultNicForVM(NETWORK_ID)).thenReturn(nicvo); - - final VirtualMachine vm = mock(VirtualMachine.class); - when(vm.getId()).thenReturn(NETWORK_ID); - when(vm.getType()).thenReturn(VirtualMachine.Type.User); - - final VirtualMachineProfile vmProfile = mock(VirtualMachineProfile.class); - when(vmProfile.getType()).thenReturn(VirtualMachine.Type.User); - when(vmProfile.getInstanceName()).thenReturn(""); - when(vmProfile.getUuid()).thenReturn("aaaa-bbbbb"); - when(vmProfile.getVirtualMachine()).thenReturn(vm); - - NicProfile nicProfile = mock(NicProfile.class); - when(nicProfile.getUuid()).thenReturn("aaa-bbbb"); - when(nicProfile.getId()).thenReturn(NETWORK_ID); - when(nicProfile.getMacAddress()).thenReturn("aa-aa-aa-aa-aa-aa"); - - final NetworkOfferingVO ntwkoffering = mock(NetworkOfferingVO.class); - when(ntwkoffering.getId()).thenReturn(NETWORK_ID); - when(_networkOfferingDao.findById(NETWORK_ID)).thenReturn(ntwkoffering); - - when(_networkDao.acquireInLockTable(NETWORK_ID, 1200)).thenReturn(network); - when(_ipAddressDao.findByVmIdAndNetworkId(NETWORK_ID, NETWORK_ID)).thenReturn(null); - when(_domainDao.findById(NETWORK_ID)).thenReturn(mock(DomainVO.class)); - - final Answer answer = mock(Answer.class); - when(answer.getResult()).thenReturn(true); - when(_agentManager.easySend(eq(NETWORK_ID), (Command)any())).thenReturn(answer); - - final ReservationContext reservationContext = mock(ReservationContext.class); - when(reservationContext.getAccount()).thenReturn(networksAccount); - when(reservationContext.getDomain()).thenReturn(networksDomain); + final NetworkVO network = mockNetwork(); + final NicProfile nicProfile = mockNicProfile(); + final VirtualMachineProfile vmProfile = mockVirtualMachineProfile(VirtualMachine.State.Starting); when(_networkDao.findById(NETWORK_ID)).thenReturn(network); - _nuageVspGuestNetworkGuru.reserve(nicProfile, network, vmProfile, mock(DeployDestination.class), reservationContext); + _nuageVspGuestNetworkGuru.reserve(nicProfile, network, vmProfile, mock(DeployDestination.class), _reservationContext); + + verify(_agentManager).easySend(anyLong(), any(Command.class)); + } + + @Test + public void testReserveVRRollingRestart() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, URISyntaxException { + final NetworkVO network = mockNetwork(); + final NicProfile nicProfile = mockNicProfile(); + final VirtualMachineProfile vmProfile = mockVRProfile(VirtualMachine.State.Starting); + + when(_networkDao.findById(NETWORK_ID)).thenReturn(network); + + _nuageVspGuestNetworkGuru.reserve(nicProfile, network, vmProfile, mock(DeployDestination.class), _reservationContext); + + verifyZeroInteractions(_agentManager); + verify(network).setRollingRestart(true); } @Test public void testImplementNetwork() throws URISyntaxException, InsufficientVirtualNetworkCapacityException { - final NetworkVO network = mock(NetworkVO.class); - when(network.getId()).thenReturn(NETWORK_ID); - when(network.getUuid()).thenReturn("aaaaaa"); - when(network.getDataCenterId()).thenReturn(NETWORK_ID); - when(network.getNetworkOfferingId()).thenReturn(NETWORK_ID); - when(network.getPhysicalNetworkId()).thenReturn(NETWORK_ID); - when(network.getDomainId()).thenReturn(NETWORK_ID); - when(network.getAccountId()).thenReturn(NETWORK_ID); - when(network.getVpcId()).thenReturn(null); + final NetworkVO network = mockNetwork(); + when(network.getState()).thenReturn(com.cloud.network.Network.State.Implementing); - when(network.getTrafficType()).thenReturn(TrafficType.Guest); - when(network.getMode()).thenReturn(Mode.Static); - when(network.getBroadcastDomainType()).thenReturn(BroadcastDomainType.Vsp); - when(network.getBroadcastUri()).thenReturn(new URI("vsp://aaaaaa-aavvv/10.1.1.1")); - when(network.getGateway()).thenReturn("10.1.1.1"); - when(network.getCidr()).thenReturn("10.1.1.0/24"); - when(network.getName()).thenReturn("iso"); - final NetworkOffering offering = mock(NetworkOffering.class); - when(offering.getId()).thenReturn(NETWORK_ID); - when(offering.getTrafficType()).thenReturn(TrafficType.Guest); - when(offering.getTags()).thenReturn("aaaa"); - when(offering.getEgressDefaultPolicy()).thenReturn(true); + final NetworkOffering offering = mockNetworkOffering(false); - when(_networkModel.findPhysicalNetworkId(NETWORK_ID, "aaa", TrafficType.Guest)).thenReturn(NETWORK_ID); - - final ReservationContext reserveContext = mock(ReservationContext.class); - final Domain domain = mock(Domain.class); - when(domain.getId()).thenReturn(NETWORK_ID); - when(reserveContext.getDomain()).thenReturn(domain); - when(domain.getName()).thenReturn("aaaaa"); - final Account account = mock(Account.class); - when(account.getId()).thenReturn(NETWORK_ID); - when(account.getAccountId()).thenReturn(NETWORK_ID); - when(reserveContext.getAccount()).thenReturn(account); - final DomainVO domainVo = mock(DomainVO.class); - when(_domainDao.findById(NETWORK_ID)).thenReturn(domainVo); - final AccountVO accountVo = mock(AccountVO.class); - when(_accountDao.findById(NETWORK_ID)).thenReturn(accountVo); - - when(_networkDao.acquireInLockTable(NETWORK_ID, 1200)).thenReturn(network); - when(_nuageVspManager.getDnsDetails(network.getDataCenterId())).thenReturn(new ArrayList()); - when(_nuageVspManager.getGatewaySystemIds()).thenReturn(new ArrayList()); - - final DataCenter dc = mock(DataCenter.class); - when(dc.getId()).thenReturn(NETWORK_ID); final DeployDestination deployDest = mock(DeployDestination.class); - when(deployDest.getDataCenter()).thenReturn(dc); - _nuageVspGuestNetworkGuru.implement(network, offering, deployDest, reserveContext); + when(deployDest.getDataCenter()).thenReturn(_dc); + _nuageVspGuestNetworkGuru.implement(network, offering, deployDest, _reservationContext); } @Test public void testDeallocate() throws Exception { - final NetworkVO network = mock(NetworkVO.class); - when(network.getId()).thenReturn(NETWORK_ID); - when(network.getUuid()).thenReturn("aaaaaa"); - when(network.getNetworkOfferingId()).thenReturn(NETWORK_ID); - when(network.getPhysicalNetworkId()).thenReturn(NETWORK_ID); - when(network.getVpcId()).thenReturn(null); - when(network.getDomainId()).thenReturn(NETWORK_ID); - when(_networkDao.acquireInLockTable(NETWORK_ID, 1200)).thenReturn(network); - - final NetworkOfferingVO offering = mock(NetworkOfferingVO.class); - when(offering.getId()).thenReturn(NETWORK_ID); - when(offering.getTrafficType()).thenReturn(TrafficType.Guest); - when(_networkOfferingDao.findById(NETWORK_ID)).thenReturn(offering); - - final DomainVO domain = mock(DomainVO.class); - when(domain.getUuid()).thenReturn("aaaaaa"); - when(_domainDao.findById(NETWORK_ID)).thenReturn(domain); - - final NicVO nic = mock(NicVO.class); - when(nic.getId()).thenReturn(NETWORK_ID); - when(nic.getIPv4Address()).thenReturn("10.10.10.10"); - when(nic.getMacAddress()).thenReturn("c8:60:00:56:e5:58"); - when(_nicDao.findById(NETWORK_ID)).thenReturn(nic); - - final NicProfile nicProfile = mock(NicProfile.class); - when(nicProfile.getId()).thenReturn(NETWORK_ID); - when(nicProfile.getIPv4Address()).thenReturn("10.10.10.10"); - when(nicProfile.getMacAddress()).thenReturn("c8:60:00:56:e5:58"); - - final VirtualMachine vm = mock(VirtualMachine.class); - when(vm.getType()).thenReturn(VirtualMachine.Type.User); - when(vm.getState()).thenReturn(VirtualMachine.State.Expunging); - - final VirtualMachineProfile vmProfile = mock(VirtualMachineProfile.class); - when(vmProfile.getUuid()).thenReturn("aaaaaa"); - when(vmProfile.getInstanceName()).thenReturn("Test-VM"); - when(vmProfile.getVirtualMachine()).thenReturn(vm); + final NetworkVO network = mockNetwork(); + final NicProfile nicProfile = mockNicProfile(); + final VirtualMachineProfile vmProfile = mockVirtualMachineProfile(VirtualMachine.State.Expunging); _nuageVspGuestNetworkGuru.deallocate(network, nicProfile, vmProfile); } + @Test + public void testDeallocateVR() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, URISyntaxException { + final NetworkVO network = mockNetwork(); + final NicProfile nicProfile = mockNicProfile(); + final VirtualMachineProfile vmProfile = mockVRProfile(VirtualMachine.State.Expunging); + + when(_networkDao.findById(NETWORK_ID)).thenReturn(network); + + _nuageVspGuestNetworkGuru.deallocate(network, nicProfile, vmProfile); + } + + @Test + public void testDeallocateVRRollingRestart() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, URISyntaxException { + final NetworkVO network = mockNetwork(); + final NicProfile nicProfile = mockNicProfile(); + final VirtualMachineProfile vmProfile = mockVRProfile(VirtualMachine.State.Expunging); + + DomainRouterVO newVR = mock(DomainRouterVO.class); + + when(_routerDao.listByNetworkAndRole(NETWORK_ID, VirtualRouter.Role.VIRTUAL_ROUTER)).thenReturn(Collections.singletonList(newVR)); + when(_networkDao.findById(NETWORK_ID)).thenReturn(network); + + _nuageVspGuestNetworkGuru.deallocate(network, nicProfile, vmProfile); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Command.class); + verify(_agentManager, times(2)).easySend(eq(HOST_ID), argumentCaptor.capture()); + final List commands = argumentCaptor.getAllValues(); + assertThat(commands.get(0) instanceof DeallocateVmVspCommand, is(true)); + assertThat(commands.get(1) instanceof ReserveVmInterfaceVspCommand, is(true)); + } + @Test public void testTrash() throws Exception { final NetworkVO network = mock(NetworkVO.class); when(network.getId()).thenReturn(NETWORK_ID); - when(network.getUuid()).thenReturn("aaaaaa"); + when(network.getUuid()).thenReturn(NETWORK_UUID); when(network.getName()).thenReturn("trash"); - when(network.getDomainId()).thenReturn(NETWORK_ID); - when(network.getNetworkOfferingId()).thenReturn(NETWORK_ID); + when(network.getDomainId()).thenReturn(DOMAIN_ID); + when(network.getNetworkOfferingId()).thenReturn(OFFERING_ID); when(network.getPhysicalNetworkId()).thenReturn(NETWORK_ID); - when(network.getDataCenterId()).thenReturn(NETWORK_ID); + when(network.getDataCenterId()).thenReturn(DATACENTER_ID); when(network.getVpcId()).thenReturn(null); when(_networkDao.acquireInLockTable(NETWORK_ID, 1200)).thenReturn(network); - final NetworkOfferingVO offering = mock(NetworkOfferingVO.class); - when(offering.getId()).thenReturn(NETWORK_ID); - when(offering.getTrafficType()).thenReturn(TrafficType.Guest); - when(_networkOfferingDao.findById(NETWORK_ID)).thenReturn(offering); + final NetworkOffering offering = mockNetworkOffering(false); - final DomainVO domain = mock(DomainVO.class); - when(domain.getUuid()).thenReturn("aaaaaa"); - when(_domainDao.findById(NETWORK_ID)).thenReturn(domain); - - final HostVO host = mock(HostVO.class); - when(host.getId()).thenReturn(NETWORK_ID); - final NuageVspDeviceVO nuageVspDevice = mock(NuageVspDeviceVO.class); - when(nuageVspDevice.getHostId()).thenReturn(NETWORK_ID); - when(_nuageVspDao.listByPhysicalNetwork(NETWORK_ID)).thenReturn(Arrays.asList(new NuageVspDeviceVO[]{nuageVspDevice})); - when(_hostDao.findById(NETWORK_ID)).thenReturn(host); - when(_nuageVspManager.getDnsDetails(network.getDataCenterId())).thenReturn(new ArrayList()); - when(_nuageVspManager.getGatewaySystemIds()).thenReturn(new ArrayList()); + when(_nuageVspManager.getDnsDetails(network.getDataCenterId())).thenReturn(new ArrayList<>()); + when(_nuageVspManager.getGatewaySystemIds()).thenReturn(new ArrayList<>()); assertTrue(_nuageVspGuestNetworkGuru.trash(network, offering)); } + @Nonnull + private NetworkVO mockNetwork() throws URISyntaxException { + final NetworkVO network = mock(NetworkVO.class); + when(network.getId()).thenReturn(NETWORK_ID); + when(network.getUuid()).thenReturn(NETWORK_UUID); + when(network.getDataCenterId()).thenReturn(DATACENTER_ID); + when(network.getNetworkOfferingId()).thenReturn(OFFERING_ID); + when(network.getPhysicalNetworkId()).thenReturn(NETWORK_ID); + when(network.getDomainId()).thenReturn(DOMAIN_ID); + when(network.getAccountId()).thenReturn(ACCOUNT_ID); + when(network.getVpcId()).thenReturn(null); + when(network.getTrafficType()).thenReturn(TrafficType.Guest); + when(network.getMode()).thenReturn(Mode.Dhcp); + when(network.getBroadcastDomainType()).thenReturn(BroadcastDomainType.Vsp); + when(network.getBroadcastUri()).thenReturn(new URI("vsp://" + NETWORK_UUID + "/10.1.1.1")); + when(network.getGateway()).thenReturn("10.1.1.1"); + when(network.getCidr()).thenReturn("10.1.1.0/24"); + when(network.getName()).thenReturn("iso"); + + when(_networkDao.acquireInLockTable(NETWORK_ID, 1200)).thenReturn(network); + when(_networkDao.findById(NETWORK_ID)).thenReturn(network); + + return network; + } + + @Nonnull + private NetworkOffering mockNetworkOffering(boolean forVpc) { + final NetworkOfferingVO offering = mock(NetworkOfferingVO.class); + when(offering.getId()).thenReturn(OFFERING_ID); + when(offering.getUuid()).thenReturn(OFFERING_UUID); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + when(offering.getForVpc()).thenReturn(forVpc); + when(offering.getIsPersistent()).thenReturn(false); + when(offering.getTags()).thenReturn("aaaa"); + when(offering.getEgressDefaultPolicy()).thenReturn(true); + + when(_networkOfferingDao.findById(OFFERING_ID)).thenReturn(offering); + + when(_configurationManager.isOfferingForVpc(offering)).thenReturn(forVpc); + + when(_networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(OFFERING_ID, Service.Connectivity, Network.Provider.NuageVsp)).thenReturn(true); + when(_networkOfferingServiceMapDao.canProviderSupportServiceInNetworkOffering(OFFERING_ID, Service.SourceNat, Network.Provider.NuageVsp)).thenReturn(true); + + when(_networkModel.getNetworkOfferingServiceProvidersMap(OFFERING_ID)).thenReturn(ImmutableMap.of( + Service.Connectivity, Sets.newSet(Network.Provider.NuageVsp), + Service.SourceNat, Sets.newSet(Network.Provider.NuageVsp) + )); + + return offering; + } + + private DeploymentPlan mockDeploymentPlan() { + final DeploymentPlan deploymentPlan = mock(DeploymentPlan.class); + when(deploymentPlan.getDataCenterId()).thenReturn(DATACENTER_ID); + return deploymentPlan; + } + + private DataCenterVO mockDataCenter() { + DataCenterVO dc = mock(DataCenterVO.class); + when(dc.getId()).thenReturn(DATACENTER_ID); + when(dc.getUuid()).thenReturn(DATACENTER_UUID); + when(dc.getNetworkType()).thenReturn(NetworkType.Advanced); + when(dc.getGuestNetworkCidr()).thenReturn("10.1.1.1/24"); + when(_dataCenterDao.findById(DATACENTER_ID)).thenReturn(dc); + + return dc; + } + + @Nonnull + private Account getMockAccount() { + final AccountVO account = mock(AccountVO.class); + when(account.getId()).thenReturn(ACCOUNT_ID); + when(account.getAccountId()).thenReturn(ACCOUNT_ID); + when(account.getUuid()).thenReturn(ACCOUNT_UUID); + when(account.getDomainId()).thenReturn(DOMAIN_ID); + when(account.getType()).thenReturn(Account.ACCOUNT_TYPE_NORMAL); + + when(_accountDao.findById(ACCOUNT_ID)).thenReturn(account); + + return account; + } + + @Nonnull + private Domain getMockDomain() { + final DomainVO domain = mock(DomainVO.class); + when(domain.getId()).thenReturn(DOMAIN_ID); + when(domain.getUuid()).thenReturn(DOMAIN_UUID); + when(domain.getName()).thenReturn("aaaaa"); + + when(_domainDao.findById(DOMAIN_ID)).thenReturn(domain); + + return domain; + } + + @Nonnull + private VirtualMachineProfile mockVirtualMachineProfile(VirtualMachine.State state) { + final VirtualMachine vm = mock(VirtualMachine.class); + when(vm.getId()).thenReturn(VM_ID); + when(vm.getType()).thenReturn(VirtualMachine.Type.User); + when(vm.getState()).thenReturn(state); + + final VirtualMachineProfile vmProfile = mock(VirtualMachineProfile.class); + when(vmProfile.getType()).thenReturn(VirtualMachine.Type.User); + when(vmProfile.getInstanceName()).thenReturn("Test-VM"); + when(vmProfile.getUuid()).thenReturn(VM_UUID); + when(vmProfile.getVirtualMachine()).thenReturn(vm); + return vmProfile; + } + + @Nonnull + private VirtualMachineProfile mockVRProfile(VirtualMachine.State state) { + final VirtualMachine vm = mock(VirtualMachine.class); + when(vm.getId()).thenReturn(VM_ID); + when(vm.getUuid()).thenReturn(VM_UUID); + when(vm.getType()).thenReturn(VirtualMachine.Type.DomainRouter); + when(vm.getState()).thenReturn(state); + + + final VirtualMachineProfile vmProfile = mock(VirtualMachineProfile.class); + when(vmProfile.getType()).thenReturn(VirtualMachine.Type.DomainRouter); + when(vmProfile.getInstanceName()).thenReturn("Test-VR"); + when(vmProfile.getId()).thenReturn(VM_ID); + when(vmProfile.getUuid()).thenReturn(VM_UUID); + when(vmProfile.getVirtualMachine()).thenReturn(vm); + when(vmProfile.isRollingRestart()).thenReturn(true); + return vmProfile; + } + + @Nonnull + private NicProfile mockNicProfile() { + final NicVO nicvo = mock(NicVO.class); + when(nicvo.getId()).thenReturn(NIC_ID); + when(nicvo.getMacAddress()).thenReturn("c8:60:00:56:e5:58"); + when(nicvo.getIPv4Address()).thenReturn("10.10.10.10"); + when(nicvo.getUuid()).thenReturn("aaaa-fffff"); + when(nicvo.getNetworkId()).thenReturn(NETWORK_ID); + when(nicvo.getInstanceId()).thenReturn(VM_ID); + when(_nicDao.findById(NIC_ID)).thenReturn(nicvo); + when(_nicDao.findDefaultNicForVM(VM_ID)).thenReturn(nicvo); + + NicProfile nicProfile = mock(NicProfile.class); + when(nicProfile.getUuid()).thenReturn("aaa-bbbb"); + when(nicProfile.getId()).thenReturn(NIC_ID); + when(nicProfile.getMacAddress()).thenReturn("c8:60:00:56:e5:58"); + when(nicProfile.getIPv4Address()).thenReturn("10.10.10.10"); + return nicProfile; + } + + @Nonnull + private static ReservationContext getMockReservationContext(Account networksAccount, Domain networksDomain) { + final ReservationContext reservationContext = mock(ReservationContext.class); + when(reservationContext.getAccount()).thenReturn(networksAccount); + when(reservationContext.getDomain()).thenReturn(networksDomain); + return reservationContext; + } + } diff --git a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java index 7df1298e80c..9b481ed4a64 100644 --- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java @@ -216,6 +216,10 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ final Map params = new HashMap(1); params.put(VirtualMachineProfile.Param.ReProgramGuestNetworks, true); + if (network.isRollingRestart()) { + params.put(VirtualMachineProfile.Param.RollingRestart, true); + } + final RouterDeploymentDefinition routerDeploymentDefinition = routerDeploymentDefinitionBuilder.create() .setGuestNetwork(network) diff --git a/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java index 9999ee62cb8..165cb7d28dc 100644 --- a/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java +++ b/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java @@ -153,6 +153,10 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc final Map params = new HashMap(1); params.put(VirtualMachineProfile.Param.ReProgramGuestNetworks, true); + if (vpc.isRollingRestart()) { + params.put(VirtualMachineProfile.Param.RollingRestart, true); + } + final RouterDeploymentDefinition routerDeploymentDefinition = routerDeploymentDefinitionBuilder.create().setVpc(vpc).setDeployDestination(dest) .setAccountOwner(_accountMgr.getAccount(vpc.getAccountId())).setParams(params).build(); @@ -194,6 +198,10 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc final Map params = new HashMap(1); params.put(VirtualMachineProfile.Param.ReProgramGuestNetworks, true); + if (network.isRollingRestart()) { + params.put(VirtualMachineProfile.Param.RollingRestart, true); + } + final RouterDeploymentDefinition routerDeploymentDefinition = routerDeploymentDefinitionBuilder.create() .setGuestNetwork(network) .setVpc(vpc) diff --git a/server/src/test/java/com/cloud/network/element/ConfigDriveNetworkElementTest.java b/server/src/test/java/com/cloud/network/element/ConfigDriveNetworkElementTest.java index e964999ee9d..01713de389d 100644 --- a/server/src/test/java/com/cloud/network/element/ConfigDriveNetworkElementTest.java +++ b/server/src/test/java/com/cloud/network/element/ConfigDriveNetworkElementTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -41,12 +42,20 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.storage.configdrive.ConfigDrive; +import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.reflections.ReflectionUtils; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -90,6 +99,7 @@ import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; import com.google.common.collect.Maps; +@RunWith(PowerMockRunner.class) public class ConfigDriveNetworkElementTest { public static final String CLOUD_ID = "xx"; @@ -140,7 +150,7 @@ public class ConfigDriveNetworkElementTest { @InjectMocks private final ConfigDriveNetworkElement _configDrivesNetworkElement = new ConfigDriveNetworkElement(); @InjectMocks @Spy private NetworkModelImpl _networkModel = new NetworkModelImpl(); - @org.junit.Before + @Before public void setUp() throws NoSuchFieldException, IllegalAccessException { MockitoAnnotations.initMocks(this); @@ -243,7 +253,14 @@ public class ConfigDriveNetworkElementTest { } @Test + @SuppressWarnings("unchecked") + @PrepareForTest({ConfigDriveBuilder.class}) public void testAddPasswordAndUserData() throws Exception { + PowerMockito.mockStatic(ConfigDriveBuilder.class); + + Method method = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("buildConfigDrive")).iterator().next(); + PowerMockito.when(ConfigDriveBuilder.class, method).withArguments(Mockito.anyListOf(String[].class), Mockito.anyString(), Mockito.anyString()).thenReturn("content"); + final Answer answer = mock(Answer.class); final UserVmDetailVO userVmDetailVO = mock(UserVmDetailVO.class); when(agentManager.easySend(anyLong(), any(HandleConfigDriveIsoCommand.class))).thenReturn(answer); diff --git a/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java b/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java index 3462618fed9..f2e611de24f 100644 --- a/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java +++ b/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java @@ -29,10 +29,9 @@ import javax.net.SocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; -import junit.framework.TestCase; - import org.junit.Test; +import junit.framework.TestCase; import streamer.debug.MockServer; import streamer.debug.MockServer.Packet; @@ -93,7 +92,6 @@ public class MockServerTest extends TestCase { @Test public void testIsMockServerCanUpgradeConnectionToSsl() throws Exception { - final byte[] mockClientData1 = new byte[] {0x01, 0x02, 0x03}; final byte[] mockServerData1 = new byte[] {0x03, 0x02, 0x01}; @@ -161,8 +159,7 @@ public class MockServerTest extends TestCase { final SSLSocketFactory sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(socket, address.getHostName(), address.getPort(), true); - //sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites()); - sslSocket.setEnabledCipherSuites(new String[] { "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA" }); + sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites()); sslSocket.startHandshake(); InputStream is = sslSocket.getInputStream(); diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java index 125512bba91..0bb923f8053 100644 --- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java +++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java @@ -584,16 +584,19 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S StringBuffer sb = new StringBuffer(); sb.append(secStorageMountPoint); - if (!secStorageMountPoint.endsWith("/")) + if (!secStorageMountPoint.endsWith("/")) { sb.append("/"); + } sb.append(templateRelativeFolderPath); - if (!secStorageMountPoint.endsWith("/")) + if (!secStorageMountPoint.endsWith("/")) { sb.append("/"); + } sb.append(templateName); - if (!fileExtension.startsWith(".")) + if (!fileExtension.startsWith(".")) { sb.append("."); + } sb.append(fileExtension); return sb.toString(); @@ -904,7 +907,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S try { _storage.create(destFile.getAbsolutePath(), _tmpltpp); try ( // generate template.properties file - FileWriter writer = new FileWriter(metaFile); BufferedWriter bufferWriter = new BufferedWriter(writer);) { + FileWriter writer = new FileWriter(metaFile); BufferedWriter bufferWriter = new BufferedWriter(writer);) { // KVM didn't change template unique name, just used the template name passed from orchestration layer, so no need // to send template name back. bufferWriter.write("uniquename=" + destData.getName()); @@ -1450,7 +1453,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S Script command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A " + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName() - + " -K " + swift.getKey() + " download " + container + " " + rfilename + " -o " + lFullPath); + + " -K " + swift.getKey() + " download " + container + " " + rfilename + " -o " + lFullPath); OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); String result = command.execute(parser); if (result != null) { @@ -1554,7 +1557,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S Script command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A " + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName() - + " -K " + swift.getKey() + " list " + container + " " + rFilename); + + " -K " + swift.getKey() + " list " + container + " " + rFilename); OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); String result = command.execute(parser); if (result == null && parser.getLines() != null) { @@ -1576,7 +1579,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S Script command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A " + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName() - + " -K " + swift.getKey() + " delete " + container + " " + object); + + " -K " + swift.getKey() + " delete " + container + " " + object); OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); String result = command.execute(parser); if (result != null) { @@ -3316,7 +3319,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S for (Processor processor : processors.values()) { FormatInfo info = null; try { - info = processor.process(resourcePath, null, templateName, processTimeout * 1000); + info = processor.process(resourcePath, null, templateName, processTimeout * 1000); } catch (InternalErrorException e) { s_logger.error("Template process exception ", e); return e.toString(); diff --git a/test/integration/component/test_configdrive.py b/test/integration/component/test_configdrive.py index 364bec22404..3d4c7b5a52b 100644 --- a/test/integration/component/test_configdrive.py +++ b/test/integration/component/test_configdrive.py @@ -42,7 +42,8 @@ from marvin.lib.base import (Account, StaticNATRule, VirtualMachine, VPC, - VpcOffering) + VpcOffering, + Hypervisor) from marvin.lib.common import (get_domain, get_template, get_zone, @@ -145,6 +146,27 @@ class Services: "Dns": 'VirtualRouter' } }, + "shared_network_config_drive_offering": { + "name": 'shared_network_config_drive_offering', + "displaytext": 'shared_network_config_drive_offering', + "guestiptype": 'shared', + "supportedservices": 'Dhcp,UserData', + "traffictype": 'GUEST', + "specifyVlan": "True", + "specifyIpRanges": "True", + "availability": 'Optional', + "serviceProviderList": { + "Dhcp": "VirtualRouter", + "UserData": 'ConfigDrive' + } + }, + "publiciprange2": { + "gateway": "10.219.1.1", + "netmask": "255.255.255.0", + "startip": "10.219.1.2", + "endip": "10.219.1.5", + "forvirtualnetwork": "false" + }, "acl": { "network_all_1": { "name": "SharedNetwork-All-1", @@ -513,10 +535,7 @@ class ConfigDriveUtils: :rtype: str """ self.debug("Updating Service Provider ConfigDrive to %s" % new_state) - configdriveprovider = NetworkServiceProvider.list( - self.api_client, - name="ConfigDrive", - physicalnetworkid=self.vsp_physical_network.id)[0] + configdriveprovider = self.get_configdrive_provider() orig_state = configdriveprovider.state NetworkServiceProvider.update(self.api_client, configdriveprovider.id, @@ -524,6 +543,14 @@ class ConfigDriveUtils: self.validate_NetworkServiceProvider("ConfigDrive", state=new_state) return orig_state + def _get_test_data(self, key): + return self.test_data[key] + + def get_configdrive_provider(self): + return NetworkServiceProvider.list( + self.api_client, + name="ConfigDrive")[0] + def verify_network_creation(self, offering=None, offering_name=None, gateway=None, @@ -549,7 +576,7 @@ class ConfigDriveUtils: if offering is None: self.debug("Creating Nuage VSP network offering...") offering = self.create_NetworkOffering( - self.test_data["nuagevsp"][offering_name]) + self._get_test_data(offering_name)) self.validate_NetworkOffering(offering, state="Enabled") try: network = self.create_Network(offering, @@ -576,7 +603,7 @@ class ConfigDriveUtils: if offering is None: self.debug("Creating Nuage VSP VPC offering...") offering = self.create_VpcOffering( - self.test_data["nuagevsp"][offering_name]) + self._get_test_data(offering_name)) self.validate_VpcOffering(offering, state="Enabled") try: vpc = self.create_Vpc(offering, cidr='10.1.0.0/16') @@ -627,7 +654,7 @@ class ConfigDriveUtils: self.debug("SSHing into the VM %s" % vm.name) if ssh_client is None: - ssh = self.ssh_into_VM(vm, public_ip, keypair=ssh_key) + ssh = self.ssh_into_VM(vm, public_ip) else: ssh = ssh_client d = {x.name: x for x in ssh.logger.handlers} @@ -674,7 +701,10 @@ class ConfigDriveUtils: keypair=keypair) # Check VM self.check_VM_state(vm, state="Running") - self.verify_vsd_vm(vm) + + if keypair: + self.decrypt_password(vm) + # Check networks network_list = [] if isinstance(networks, list): @@ -685,10 +715,9 @@ class ConfigDriveUtils: for network in network_list: self.validate_Network(network, state="Implemented") - self.verify_vsd_network(self.domain.id, network, vpc=vpc) if acl_item is not None: - self.verify_vsd_firewall_rule(acl_item) + self.validate_firewall_rule(acl_item) return vm # nic_operation_VM - Performs NIC operations such as add, remove, and @@ -754,12 +783,21 @@ class ConfigDriveUtils: self.debug("Sshkey reset to - %s" % self.keypair.name) vm.start(self.api_client) - # reset SSH key also resets the password. - # the new password is available in VM detail, - # named "Encrypted.Password". - # It is encrypted using the SSH Public Key, - # and thus can be decrypted using the SSH Private Key + vm.details = vm_new_ssh.details + # reset SSH key also resets the password. + self.decrypt_password(vm) + + def decrypt_password(self, vm): + """Decrypt VM password + + the new password is available in VM detail, + named "Encrypted.Password". + It is encrypted using the SSH Public Key, + and thus can be decrypted using the SSH Private Key + + :type vm: VirtualMachine + """ try: from base64 import b64decode from Crypto.PublicKey import RSA @@ -768,7 +806,7 @@ class ConfigDriveUtils: key = RSA.importKey(pkfile.read()) cipher = PKCS1_v1_5.new(key) new_password = cipher.decrypt( - b64decode(vm_new_ssh.details['Encrypted.Password']), None) + b64decode(vm.details['Encrypted.Password']), None) if new_password: vm.password = new_password else: @@ -776,7 +814,6 @@ class ConfigDriveUtils: except: self.debug("Failed to decrypt new password") - def add_subnet_verify(self, network, services): """verify required nic is present in the VM""" @@ -806,6 +843,9 @@ class ConfigDriveUtils: ) return addedsubnet + def ssh_into_VM(self, vm, public_ip, keypair): + pass + class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): """Test user data and password reset functionality @@ -838,6 +878,9 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): cls.api_client, cls.test_data["service_offering"]) cls._cleanup = [cls.service_offering] + + hypervisors = Hypervisor.list(cls.api_client, zoneid=cls.zone.id) + cls.isSimulator = any(h.name == "Simulator" for h in hypervisors) return def setUp(self): @@ -948,6 +991,39 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): self.debug("Successfully validated the creation and state of Network " "Service Provider - %s" % provider_name) + # validate_PublicIPAddress - Validates if the given public IP address is in + # the expected state form the list of fetched public IP addresses + def validate_PublicIPAddress(self, public_ip, network, static_nat=False, + vm=None): + """Validates the Public IP Address""" + self.debug("Validating the assignment and state of public IP address " + "- %s" % public_ip.ipaddress.ipaddress) + public_ips = PublicIPAddress.list(self.api_client, + id=public_ip.ipaddress.id, + networkid=network.id, + isstaticnat=static_nat, + listall=True + ) + self.assertEqual(isinstance(public_ips, list), True, + "List public IP for network should return a " + "valid list" + ) + self.assertEqual(public_ips[0].ipaddress, + public_ip.ipaddress.ipaddress, + "List public IP for network should list the assigned " + "public IP address" + ) + self.assertEqual(public_ips[0].state, "Allocated", + "Assigned public IP is not in the allocated state" + ) + if static_nat and vm: + self.assertEqual(public_ips[0].virtualmachineid, vm.id, + "Static NAT rule is not enabled for the VM on " + "the assigned public IP" + ) + self.debug("Successfully validated the assignment and state of public " + "IP address - %s" % public_ip.ipaddress.ipaddress) + # create_NetworkOffering - Creates Network offering def create_NetworkOffering(self, net_offering, suffix=None, conserve_mode=False): @@ -1095,7 +1171,8 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): % vpc.name) # ssh_into_VM - Gets into the shell of the given VM using its public IP - def ssh_into_VM(self, vm, public_ip, reconnect=True, negative_test=False): + def ssh_into_VM(self, vm, public_ip, reconnect=True, + negative_test=False, keypair=None): self.debug("SSH into VM with ID - %s on public IP address - %s" % (vm.id, public_ip.ipaddress.ipaddress)) tries = 1 if negative_test else 3 @@ -1782,22 +1859,18 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): metadata=True, userdata=expected_user_data, ssh_key=self.keypair) - vpc_public_ip_2 = \ - self.acquire_PublicIPAddress(create_tiernetwork2.network, - create_vpc.vpc) - self.create_StaticNatRule_For_VM(vm, vpc_public_ip_2, - create_tiernetwork2.network) + vm.password = vm.resetPassword(self.api_client) self.debug("Password reset to - %s" % vm.password) self.debug("VM - %s password - %s !" % (vm.name, vm.password)) - self.verify_config_drive_content(vm, vpc_public_ip_2, + self.verify_config_drive_content(vm, vpc_public_ip_1, self.PasswordTest(vm.password), metadata=True, userdata=expected_user_data, ssh_key=self.keypair) expected_user_data1 = self.update_userdata(vm, "hellomultinicvm1") - self.verify_config_drive_content(vm, vpc_public_ip_2, + self.verify_config_drive_content(vm, vpc_public_ip_1, self.PasswordTest(vm.password), userdata=expected_user_data1, ssh_key=self.keypair) @@ -1807,6 +1880,14 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): self.nic_operation_VM(vm, create_tiernetwork2.network, operation="update") + vm.stop(self.api_client) + vm.start(self.api_client) + vpc_public_ip_2 = \ + self.acquire_PublicIPAddress(create_tiernetwork2.network, + create_vpc.vpc) + self.create_StaticNatRule_For_VM(vm, vpc_public_ip_2, + create_tiernetwork2.network) + self.verify_config_drive_content(vm, vpc_public_ip_2, self.PasswordTest(vm.password), metadata=True, diff --git a/test/integration/plugins/nuagevsp/test_nuage_configdrive.py b/test/integration/plugins/nuagevsp/test_nuage_configdrive.py index 2a5495af4c9..53a22bcb1a7 100644 --- a/test/integration/plugins/nuagevsp/test_nuage_configdrive.py +++ b/test/integration/plugins/nuagevsp/test_nuage_configdrive.py @@ -204,6 +204,15 @@ class TestNuageConfigDrive(nuageTestCase, ConfigDriveUtils): def validate_StaticNat_rule_For_VM(self, public_ip, network, vm): self.verify_vsd_floating_ip(network, vm, public_ip.ipaddress) + def _get_test_data(self, key): + return self.test_data["nuagevsp"][key] + + def get_configdrive_provider(self): + return NetworkServiceProvider.list( + self.api_client, + name="ConfigDrive", + physicalnetworkid=self.vsp_physical_network.id)[0] + def create_guest_vm(self, networks, acl_item=None, vpc=None, keypair=None): vm = self.create_VM( diff --git a/test/integration/plugins/nuagevsp/test_nuage_vpc_network.py b/test/integration/plugins/nuagevsp/test_nuage_vpc_network.py index e1b51287096..167559ad3a3 100644 --- a/test/integration/plugins/nuagevsp/test_nuage_vpc_network.py +++ b/test/integration/plugins/nuagevsp/test_nuage_vpc_network.py @@ -20,7 +20,7 @@ Nuage VSP SDN plugin """ # Import Local Modules from nuageTestCase import nuageTestCase -from marvin.lib.base import Account +from marvin.lib.base import Account, VPC # Import System Modules from nose.plugins.attrib import attr @@ -118,6 +118,12 @@ class TestNuageVpcNetwork(nuageTestCase): # VSD verification for ACL item self.verify_vsd_firewall_rule(acl_item) + self.restart_Vpc(vpc, cleanup=True) + + self.validate_Network(vpc_network, state="Implemented") + vr = self.get_Router(vpc_network) + self.verify_vsd_router(vr) + @attr( tags=["advanced", "nuagevsp", "multizone"], required_hardware="false") def test_nuage_vpc_network_multizone(self): diff --git a/test/integration/plugins/nuagevsp/test_nuage_vsp.py b/test/integration/plugins/nuagevsp/test_nuage_vsp.py index 54656fd1694..82160f03a26 100644 --- a/test/integration/plugins/nuagevsp/test_nuage_vsp.py +++ b/test/integration/plugins/nuagevsp/test_nuage_vsp.py @@ -19,7 +19,7 @@ """ # Import Local Modules from nuageTestCase import nuageTestCase -from marvin.lib.base import Account, Nuage +from marvin.lib.base import Account, Nuage, Network from marvin.cloudstackAPI import deleteNuageVspDevice # Import System Modules from nose.plugins.attrib import attr @@ -158,7 +158,7 @@ class TestNuageVsp(nuageTestCase): "Physical Network...") self.validate_NuageVspDevice() - @attr(tags=["advanced", "nuagevsp"], required_hardware="false") + @attr(tags=["advanced", "nuagevsp", "isolated"], required_hardware="false") def test_nuage_vsp(self): """ Test Nuage VSP SDN plugin with basic Isolated Network functionality """ @@ -223,6 +223,12 @@ class TestNuageVsp(nuageTestCase): # VSD verification self.verify_vsd_vm(vm_2) + Network.restart(network, self.api_client, cleanup=True) + + self.validate_Network(network, state="Implemented") + vr = self.get_Router(network) + self.verify_vsd_router(vr) + # Deleting the network self.debug("Deleting the Isolated Network with Nuage VSP Isolated " "Network offering...") diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java index 22bfafc2bad..74cef80dbe5 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -992,7 +992,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { if (s_logger.isTraceEnabled()) s_logger.trace("vCenter API trace - getHyperHostNetworkSummary() done(failed)"); - throw new Exception("Uanble to find management port group " + managementPortGroup); + throw new Exception("Unable to find management port group " + managementPortGroup); } @Override