configdrive: fix some failures in tests/component/test_configdrive.py (#5144)

* server: fix failed to apply userdata when enable static nat

* server: fix cannot expunge vm as applyUserdata fails

* configdrive: fix ISO is not recognized when plug a new nic

* configdrive: detach and attach configdrive ISO as it is changed when plug a new nic or migrate vm

* configdrive test: (1) password file does not exists in recreated ISO; (2) vm hostname should be changed after migration

* configdrive: use centos55 template with sshkey and configdrive support

* configdrive: disklabel is 'config-2' for configdrive ISO

* configdrive: use copy for configdrive ISO and move for other template/volume/iso

* configdrive: use public-keys.txt

* configdrive test: fix (1) update_template ; (2) ssh into vm by keypair
This commit is contained in:
Wei Zhou 2021-07-15 09:19:37 +02:00 committed by GitHub
parent 0406e27af3
commit cf0f1feb5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 23 deletions

View File

@ -303,6 +303,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
protected String _agentHooksVmOnStopScript = "libvirt-vm-state-change.groovy"; protected String _agentHooksVmOnStopScript = "libvirt-vm-state-change.groovy";
protected String _agentHooksVmOnStopMethod = "onStop"; protected String _agentHooksVmOnStopMethod = "onStop";
private static final String CONFIG_DRIVE_ISO_DISK_LABEL = "hdd";
private static final int CONFIG_DRIVE_ISO_DEVICE_ID = 4;
protected File _qemuSocketsPath; protected File _qemuSocketsPath;
private final String _qemuGuestAgentSocketName = "org.qemu.guest_agent.0"; private final String _qemuGuestAgentSocketName = "org.qemu.guest_agent.0";
@ -2756,6 +2758,32 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return _storagePoolMgr; return _storagePoolMgr;
} }
public void detachAndAttachConfigDriveISO(final Connect conn, final String vmName) {
// detach and re-attach configdrive ISO
List<DiskDef> disks = getDisks(conn, vmName);
DiskDef configdrive = null;
for (DiskDef disk : disks) {
if (disk.getDeviceType() == DiskDef.DeviceType.CDROM && disk.getDiskLabel() == CONFIG_DRIVE_ISO_DISK_LABEL) {
configdrive = disk;
}
}
if (configdrive != null) {
try {
String result = attachOrDetachISO(conn, vmName, configdrive.getDiskPath(), false, CONFIG_DRIVE_ISO_DEVICE_ID);
if (result != null) {
s_logger.warn("Detach ConfigDrive ISO with result: " + result);
}
result = attachOrDetachISO(conn, vmName, configdrive.getDiskPath(), true, CONFIG_DRIVE_ISO_DEVICE_ID);
if (result != null) {
s_logger.warn("Attach ConfigDrive ISO with result: " + result);
}
} catch (final LibvirtException | InternalErrorException | URISyntaxException e) {
final String msg = "Detach and attach ConfigDrive ISO failed due to " + e.toString();
s_logger.warn(msg, e);
}
}
}
public synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach, final Integer diskSeq) throws LibvirtException, URISyntaxException, public synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach, final Integer diskSeq) throws LibvirtException, URISyntaxException,
InternalErrorException { InternalErrorException {
final DiskDef iso = new DiskDef(); final DiskDef iso = new DiskDef();

View File

@ -76,6 +76,7 @@ import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper; import com.cloud.resource.ResourceWrapper;
import com.cloud.utils.Ternary; import com.cloud.utils.Ternary;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
import com.google.common.base.Strings; import com.google.common.base.Strings;
@ResourceWrapper(handles = MigrateCommand.class) @ResourceWrapper(handles = MigrateCommand.class)
@ -180,6 +181,10 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
dconn = libvirtUtilitiesHelper.retrieveQemuConnection(destinationUri); dconn = libvirtUtilitiesHelper.retrieveQemuConnection(destinationUri);
if (to.getType() == VirtualMachine.Type.User) {
libvirtComputingResource.detachAndAttachConfigDriveISO(conn, vmName);
}
//run migration in thread so we can monitor it //run migration in thread so we can monitor it
s_logger.info("Live migration of instance " + vmName + " initiated to destination host: " + dconn.getURI()); s_logger.info("Live migration of instance " + vmName + " initiated to destination host: " + dconn.getURI());
final ExecutorService executor = Executors.newFixedThreadPool(1); final ExecutorService executor = Executors.newFixedThreadPool(1);

View File

@ -72,6 +72,10 @@ public final class LibvirtPlugNicCommandWrapper extends CommandWrapper<PlugNicCo
libvirtComputingResource.applyDefaultNetworkRulesOnNic(conn, vmName, vmId, nic, false, false); libvirtComputingResource.applyDefaultNetworkRulesOnNic(conn, vmName, vmId, nic, false, false);
} }
if (vmType == VirtualMachine.Type.User) {
libvirtComputingResource.detachAndAttachConfigDriveISO(conn, vmName);
}
return new PlugNicAnswer(command, true, "success"); return new PlugNicAnswer(command, true, "success");
} catch (final LibvirtException e) { } catch (final LibvirtException e) {
final String msg = " Plug Nic failed due to " + e.toString(); final String msg = " Plug Nic failed due to " + e.toString();

View File

@ -139,6 +139,16 @@ create_from_file() {
} }
copy_from_file() {
local tmpltfs=$1
local tmpltimg=$2
local tmpltname=$3
[ -n "$verbose" ] && echo "Copying to $tmpltfs/$tmpltname...could take a while" >&2
cp -rf $tmpltimg /$tmpltfs/$tmpltname
rm -rf $tmpltimg
}
tflag= tflag=
nflag= nflag=
fflag= fflag=
@ -228,7 +238,11 @@ fi
imgsize=$(ls -l $tmpltimg2| awk -F" " '{print $5}') imgsize=$(ls -l $tmpltimg2| awk -F" " '{print $5}')
create_from_file $tmpltfs $tmpltimg2 $tmpltname if [ "$descr" = "configDrive" ] && [ "$Sflag" = "" ];then
copy_from_file $tmpltfs $tmpltimg2 $tmpltname
else
create_from_file $tmpltfs $tmpltimg2 $tmpltname
fi
touch /$tmpltfs/volume.properties touch /$tmpltfs/volume.properties
rollback_if_needed $tmpltfs $? "Failed to create volume.properties file" rollback_if_needed $tmpltfs $? "Failed to create volume.properties file"

View File

@ -630,20 +630,23 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
} }
protected void applyUserData(long vmId, Network network, Nic guestNic) throws ResourceUnavailableException { protected void applyUserData(long vmId, Network network, Nic guestNic) throws ResourceUnavailableException {
UserVmVO vm = _vmDao.findById(vmId);
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
NicProfile nicProfile = new NicProfile(guestNic, network, null, null, null,
_networkModel.isSecurityGroupSupportedInNetwork(network),
_networkModel.getNetworkTag(template.getHypervisorType(), network));
VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm);
UserDataServiceProvider element = _networkModel.getUserDataUpdateProvider(network); UserDataServiceProvider element = _networkModel.getUserDataUpdateProvider(network);
if (element == null) { if (element == null) {
s_logger.error("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update"); s_logger.error("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update");
} else { } else {
boolean result = element.saveUserData(network, nicProfile, vmProfile); UserVmVO vm = _vmDao.findById(vmId);
if (!result) { try {
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
NicProfile nicProfile = new NicProfile(guestNic, network, null, null, null,
_networkModel.isSecurityGroupSupportedInNetwork(network),
_networkModel.getNetworkTag(template.getHypervisorType(), network));
VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm);
if (!element.saveUserData(network, nicProfile, vmProfile)) {
s_logger.error("Failed to update userdata for vm " + vm + " and nic " + guestNic); s_logger.error("Failed to update userdata for vm " + vm + " and nic " + guestNic);
} }
} catch (Exception e) {
s_logger.error("Failed to update userdata for vm " + vm + " and nic " + guestNic + " due to " + e.getMessage(), e);
}
} }
} }

View File

@ -31,7 +31,7 @@ mountdir=$(mktemp -d)
# If lable name is other than config, please change the below line as required # If lable name is other than config, please change the below line as required
DefaultDisk=/dev/disk/by-label/config-2 DefaultDisk=/dev/disk/by-label/config-2
SSHKey_File=$mountdir/cloudstack/metadata/public_keys.txt SSHKey_File=$mountdir/cloudstack/metadata/public-keys.txt
keys_received=0 keys_received=0
function prepare_mount function prepare_mount
@ -44,7 +44,7 @@ function prepare_mount
if [ -e $DefaultDisk ]; then if [ -e $DefaultDisk ]; then
Disk=$DefaultDisk Disk=$DefaultDisk
else else
BLOCK_DEVICE=$(blkid -t LABEL='config' /dev/hd? /dev/sd? /dev/xvd? /dev/vd? -o device) BLOCK_DEVICE=$(blkid -t LABEL='config-2' /dev/hd? /dev/sd? /dev/xvd? /dev/vd? -o device)
if [ -n $BLOCK_DEVICE ]; then if [ -n $BLOCK_DEVICE ]; then
Disk=$BLOCK_DEVICE Disk=$BLOCK_DEVICE
else else

View File

@ -53,7 +53,8 @@ from marvin.lib.base import (Account,
Hypervisor, Template) Hypervisor, Template)
from marvin.lib.common import (get_domain, from marvin.lib.common import (get_domain,
get_template, get_template,
get_zone, get_test_template, get_zone,
get_test_template,
is_config_suitable) is_config_suitable)
from marvin.lib.utils import random_gen from marvin.lib.utils import random_gen
# Import System Modules # Import System Modules
@ -104,12 +105,12 @@ class Services:
self.services = { self.services = {
"test_templates": { "test_templates": {
"kvm": { "kvm": {
"name": "Centos-5.5-configdrive", "name": "Centos-5.5-sshkey-and-configdrive",
"displaytext": "ConfigDrive enabled CentOS", "displaytext": "SSHkey and ConfigDrive enabled CentOS",
"format": "qcow2", "format": "qcow2",
"hypervisor": "kvm", "hypervisor": "kvm",
"ostype": "CentOS 5.5 (64-bit)", "ostype": "CentOS 5.5 (64-bit)",
"url": "http://people.apache.org/~fmaximus/centos55-extended.qcow2.bz2", "url": "http://people.apache.org/~weizhou/centos55-sshkey-configdrive.qcow2.bz2",
"requireshvm": "False", "requireshvm": "False",
"ispublic": "True", "ispublic": "True",
"isextractable": "True" "isextractable": "True"
@ -653,8 +654,7 @@ class ConfigDriveUtils:
orig_state = self.template.passwordenabled orig_state = self.template.passwordenabled
self.debug("Updating guest VM template to password enabled " self.debug("Updating guest VM template to password enabled "
"from %s to %s" % (orig_state, new_state)) "from %s to %s" % (orig_state, new_state))
if orig_state != new_state: self.update_template(passwordenabled=new_state)
self.update_template(passwordenabled=new_state)
self.assertEqual(self.template.passwordenabled, new_state, self.assertEqual(self.template.passwordenabled, new_state,
"Guest VM template is not password enabled") "Guest VM template is not password enabled")
return orig_state return orig_state
@ -850,7 +850,7 @@ class ConfigDriveUtils:
self.debug("SSHing into the VM %s" % vm.name) self.debug("SSHing into the VM %s" % vm.name)
ssh = self.ssh_into_VM(vm, public_ip, reconnect=reconnect) ssh = self.ssh_into_VM(vm, public_ip, reconnect=reconnect, keypair=vm.key_pair)
d = {x.name: x for x in ssh.logger.handlers} d = {x.name: x for x in ssh.logger.handlers}
ssh.logger.handlers = list(d.values()) ssh.logger.handlers = list(d.values())
@ -974,6 +974,7 @@ class ConfigDriveUtils:
vm.add_nic(self.api_client, network.id) vm.add_nic(self.api_client, network.id)
self.debug("Added NIC in VM with ID - %s and network with ID - %s" self.debug("Added NIC in VM with ID - %s and network with ID - %s"
% (vm.id, network.id)) % (vm.id, network.id))
vm.password_test = ConfigDriveUtils.PasswordTest(expect_pw=False)
def unplug_nic(self, vm, network): def unplug_nic(self, vm, network):
nic = self._find_nic(vm, network) nic = self._find_nic(vm, network)
@ -1530,12 +1531,14 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
self.debug("SSH into VM with ID - %s on public IP address - %s" % self.debug("SSH into VM with ID - %s on public IP address - %s" %
(vm.id, public_ip.ipaddress.ipaddress)) (vm.id, public_ip.ipaddress.ipaddress))
tries = 1 if negative_test else 3 tries = 1 if negative_test else 3
private_key_file_location = keypair.private_key_file if keypair else None
@retry(tries=tries) @retry(tries=tries)
def retry_ssh(): def retry_ssh():
ssh_client = vm.get_ssh_client( ssh_client = vm.get_ssh_client(
ipaddress=public_ip.ipaddress.ipaddress, ipaddress=public_ip.ipaddress.ipaddress,
reconnect=reconnect, reconnect=reconnect,
keyPairFileLocation=private_key_file_location,
retries=3 if negative_test else 30 retries=3 if negative_test else 30
) )
self.debug("Successful to SSH into VM with ID - %s on " self.debug("Successful to SSH into VM with ID - %s on "
@ -1702,6 +1705,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
"%s to Host: %s" % (vm.id, host.id)) "%s to Host: %s" % (vm.id, host.id))
try: try:
vm.migrate(self.api_client, hostid=host.id) vm.migrate(self.api_client, hostid=host.id)
vm.password_test = ConfigDriveUtils.PasswordTest(expect_pw=False)
except Exception as e: except Exception as e:
self.fail("Failed to migrate instance, %s" % e) self.fail("Failed to migrate instance, %s" % e)
self.debug("Migrated VM with ID: " self.debug("Migrated VM with ID: "
@ -1917,7 +1921,8 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
# ===================================================================== # =====================================================================
self.debug("+++ Scenario: " self.debug("+++ Scenario: "
"update userdata and reset password after migrate") "update userdata and reset password after migrate")
self.migrate_VM(vm1) host = self.migrate_VM(vm1)
vm1.hostname = host.name
self.then_config_drive_is_as_expected(vm1, public_ip_1, metadata=True) self.then_config_drive_is_as_expected(vm1, public_ip_1, metadata=True)
self.debug("Updating userdata after migrating VM - %s" % vm1.name) self.debug("Updating userdata after migrating VM - %s" % vm1.name)
self.update_and_validate_userdata(vm1, "hello after migrate", self.update_and_validate_userdata(vm1, "hello after migrate",
@ -1955,7 +1960,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
# ===================================================================== # =====================================================================
self.debug("+++ Scenario: " self.debug("+++ Scenario: "
"Update Userdata on a VM that is not password enabled") "Update Userdata on a VM that is not password enabled")
self.update_template(passwordenabled=False) self.given_template_password_enabled_is(False)
vm1 = self.when_I_deploy_a_vm_with_keypair_in(network1) vm1 = self.when_I_deploy_a_vm_with_keypair_in(network1)
public_ip_1 = \ public_ip_1 = \
@ -2112,7 +2117,8 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
# ===================================================================== # =====================================================================
self.debug("+++ Scenario: " self.debug("+++ Scenario: "
"update userdata and reset password after migrate") "update userdata and reset password after migrate")
self.migrate_VM(vm) host = self.migrate_VM(vm)
vm.hostname = host.name
self.then_config_drive_is_as_expected(vm, public_ip_1, metadata=True) self.then_config_drive_is_as_expected(vm, public_ip_1, metadata=True)
self.update_and_validate_userdata(vm, "hello migrate", public_ip_1) self.update_and_validate_userdata(vm, "hello migrate", public_ip_1)
@ -2150,7 +2156,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
self.debug("+++ Scenario: " self.debug("+++ Scenario: "
"Update Userdata on a VM that is not password enabled") "Update Userdata on a VM that is not password enabled")
self.update_template(passwordenabled=False) self.given_template_password_enabled_is(False)
vm = self.when_I_deploy_a_vm(network1, vm = self.when_I_deploy_a_vm(network1,
keypair=self.keypair.name) keypair=self.keypair.name)
@ -2285,7 +2291,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
self.delete(vm1, expunge=True) self.delete(vm1, expunge=True)
self.given_config_drive_provider_is("Enabled") self.given_config_drive_provider_is("Enabled")
self.update_template(passwordenabled=False) self.given_template_password_enabled_is(False)
vm1 = self.create_VM( vm1 = self.create_VM(
[shared_network.network], [shared_network.network],
@ -2362,6 +2368,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
self.debug("+++Deploy VM in the created Isolated network " self.debug("+++Deploy VM in the created Isolated network "
"with user data provider as configdrive") "with user data provider as configdrive")
self.given_template_password_enabled_is(True)
vm1 = self.when_I_deploy_a_vm(network1) vm1 = self.when_I_deploy_a_vm(network1)
public_ip_1 = self.when_I_create_a_static_nat_ip_to(vm1, network1) public_ip_1 = self.when_I_create_a_static_nat_ip_to(vm1, network1)
@ -2476,6 +2483,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
# ===================================================================== # =====================================================================
self.debug("+++ Scenario: " self.debug("+++ Scenario: "
"Deploy VM in the Tier 1 with user data") "Deploy VM in the Tier 1 with user data")
self.given_template_password_enabled_is(True)
vm = self.when_I_deploy_a_vm(network1) vm = self.when_I_deploy_a_vm(network1)
public_ip_1 = self.when_I_create_a_static_nat_ip_to(vm, network1) public_ip_1 = self.when_I_create_a_static_nat_ip_to(vm, network1)