From caf5857434e9faae29b06b44337357ee413d906f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=93=9C=40=E3=82=A2=E3=82=A4=E3=82=BD=E3=83=9E=3A?= =?UTF-8?q?=E3=82=86=E3=82=8B=E3=82=86=E3=82=8A=E3=81=AF=E3=81=84=E3=81=84?= =?UTF-8?q?=E3=81=9E?= Date: Sun, 27 May 2018 21:54:25 +0900 Subject: [PATCH 1/4] Fix two typos (from uanble to unable). (#2676) Signed-off-by: carrot031 --- .../hypervisor/hyperv/resource/HypervDirectConnectResource.java | 2 +- vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java index bc955c809b1..1f9ad02b61f 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java +++ b/plugins/hypervisors/hyperv/src/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/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java index 22bfafc2bad..74cef80dbe5 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/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 From d46fa6e1980a74f83d41d90eca53ed59bae1f282 Mon Sep 17 00:00:00 2001 From: Frank Maximus Date: Thu, 31 May 2018 11:53:59 +0200 Subject: [PATCH 2/4] Fix test_configdrive.py (#2673) --- .../integration/component/test_configdrive.py | 133 ++++++++++++++---- .../nuagevsp/test_nuage_configdrive.py | 9 ++ 2 files changed, 116 insertions(+), 26 deletions(-) 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( From 9b83337658ede7f55bc404f869ae996d929ae9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Weing=C3=A4rtner?= Date: Mon, 4 Jun 2018 08:20:09 -0300 Subject: [PATCH 3/4] Create unit test cases for 'ConfigDriveBuilder' class (#2674) * Create unit test cases for 'ConfigDriveBuilder' class * add method 'getProgramToGenerateIso' as suggested by rohit and Daan * fix encoding for base64 to StandardCharsets.US_ASCII * fix MockServerTest.testIsMockServerCanUpgradeConnectionToSsl() This is another method that is causing Jenkins to fail for almost a month --- .../storage/configdrive/ConfigDrive.java | 11 +- .../configdrive/ConfigDriveBuilder.java | 328 +++++++----- .../configdrive/ConfigDriveBuilderTest.java | 486 +++++++++++++++++- .../storage/configdrive/ConfigDriveTest.java | 32 ++ .../ConfigDriveNetworkElementTest.java | 19 +- .../test/java/rdpclient/MockServerTest.java | 7 +- .../resource/NfsSecondaryStorageResource.java | 19 +- 7 files changed, 738 insertions(+), 164 deletions(-) create mode 100644 engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveTest.java diff --git a/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDrive.java b/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDrive.java index ec461991537..07cfdc883ca 100644 --- a/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDrive.java +++ b/engine/storage/configdrive/src/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/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java b/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java index d847aa1d1d7..dc6b8893495 100644 --- a/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java +++ b/engine/storage/configdrive/src/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/test/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java b/engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java index 50a4384d5c8..96032738edc 100644 --- a/engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java +++ b/engine/storage/configdrive/test/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/server/test/com/cloud/network/element/ConfigDriveNetworkElementTest.java b/server/test/com/cloud/network/element/ConfigDriveNetworkElementTest.java index e964999ee9d..01713de389d 100644 --- a/server/test/com/cloud/network/element/ConfigDriveNetworkElementTest.java +++ b/server/test/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/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java b/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java index 125512bba91..0bb923f8053 100644 --- a/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java +++ b/services/secondary-storage/server/src/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(); From 8798014ca8ce921bcaec747291c36fbbd1ab6b7e Mon Sep 17 00:00:00 2001 From: Frank Maximus Date: Wed, 6 Jun 2018 08:47:10 +0200 Subject: [PATCH 4/4] CLOUDSTACK-10377: Fix Network restart for Nuage (#2672) Changes in PR #2508 have caused network restart to fail in a Nuage setup, as the new VR takes the same IP as the old one, and the old VR is still running. Nuage doesn't support multiple VM's having the same IP. We delay provisioning the interfaces in VSD until the old VR interface is released. --- .../com/cloud/vm/VirtualMachineProfile.java | 3 + .../cloud/vm/VirtualMachineProfileImpl.java | 5 + .../network/element/NuageVspElement.java | 85 +-- .../guru/NuageVspGuestNetworkGuru.java | 85 ++- .../guru/NuageVspGuestNetworkGuruTest.java | 554 ++++++++++-------- .../network/element/VirtualRouterElement.java | 4 + .../element/VpcVirtualRouterElement.java | 8 + .../nuagevsp/test_nuage_vpc_network.py | 8 +- .../plugins/nuagevsp/test_nuage_vsp.py | 10 +- 9 files changed, 438 insertions(+), 324 deletions(-) diff --git a/api/src/com/cloud/vm/VirtualMachineProfile.java b/api/src/com/cloud/vm/VirtualMachineProfile.java index ed02dcb9c67..977e27eb14f 100644 --- a/api/src/com/cloud/vm/VirtualMachineProfile.java +++ b/api/src/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/com/cloud/vm/VirtualMachineProfileImpl.java b/engine/components-api/src/com/cloud/vm/VirtualMachineProfileImpl.java index 0afca735a13..4d03396c1cb 100644 --- a/engine/components-api/src/com/cloud/vm/VirtualMachineProfileImpl.java +++ b/engine/components-api/src/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/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java b/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java index d5496b0157a..48c3d88d0d0 100644 --- a/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java +++ b/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java @@ -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/com/cloud/network/guru/NuageVspGuestNetworkGuru.java b/plugins/network-elements/nuage-vsp/src/com/cloud/network/guru/NuageVspGuestNetworkGuru.java index 9ecefe49087..8d53f0f4268 100644 --- a/plugins/network-elements/nuage-vsp/src/com/cloud/network/guru/NuageVspGuestNetworkGuru.java +++ b/plugins/network-elements/nuage-vsp/src/com/cloud/network/guru/NuageVspGuestNetworkGuru.java @@ -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/test/com/cloud/network/guru/NuageVspGuestNetworkGuruTest.java b/plugins/network-elements/nuage-vsp/test/com/cloud/network/guru/NuageVspGuestNetworkGuruTest.java index b1d6771a36a..0708202c215 100644 --- a/plugins/network-elements/nuage-vsp/test/com/cloud/network/guru/NuageVspGuestNetworkGuruTest.java +++ b/plugins/network-elements/nuage-vsp/test/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/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index 7df1298e80c..9b481ed4a64 100644 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/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/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java index 9999ee62cb8..165cb7d28dc 100644 --- a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java +++ b/server/src/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/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...")