orchestration,hypervisor: allow custom manufacture, product for vm (#9163)

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2024-08-22 20:49:04 +05:30 committed by GitHub
parent b215abc30a
commit 1e12a80210
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 149 additions and 24 deletions

View File

@ -84,6 +84,8 @@ public class VirtualMachineTO {
Map<String, String> extraConfig = new HashMap<>();
Map<Long, String> networkIdToNetworkNameMap = new HashMap<>();
DeployAsIsInfoTO deployAsIsInfo;
String metadataManufacturer;
String metadataProductName;
public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader,
String os, boolean enableHA, boolean limitCpuUse, String vncPassword) {
@ -429,6 +431,22 @@ public class VirtualMachineTO {
this.deployAsIsInfo = deployAsIsInfo;
}
public String getMetadataManufacturer() {
return metadataManufacturer;
}
public void setMetadataManufacturer(String metadataManufacturer) {
this.metadataManufacturer = metadataManufacturer;
}
public String getMetadataProductName() {
return metadataProductName;
}
public void setMetadataProductName(String metadataProductName) {
this.metadataProductName = metadataProductName;
}
@Override
public String toString() {
return String.format("VM {id: \"%s\", name: \"%s\", uuid: \"%s\", type: \"%s\"}", id, name, uuid, type);

View File

@ -87,6 +87,20 @@ public interface VirtualMachineManager extends Manager {
ConfigKey<String> MetadataCustomCloudName = new ConfigKey<>("Advanced", String.class, "metadata.custom.cloud.name", "",
"If provided, a custom cloud-name in cloud-init metadata", true, ConfigKey.Scope.Zone);
ConfigKey<String> VmMetadataManufacturer = new ConfigKey<>("Advanced", String.class,
"vm.metadata.manufacturer", "Apache Software Foundation",
"If provided, a custom manufacturer will be used in the instance metadata. When an empty" +
"value is set then default manufacturer will be 'Apache Software Foundation'. " +
"A custom manufacturer may break cloud-init functionality with CloudStack datasource. Please " +
"refer documentation", true, ConfigKey.Scope.Zone);
ConfigKey<String> VmMetadataProductName = new ConfigKey<>("Advanced", String.class,
"vm.metadata.product", "",
"If provided, a custom product name will be used in the instance metadata. When an empty" +
"value is set then default product name will be 'CloudStack <HYPERVISIOR_NAME> Hypervisor'. " +
"A custom product name may break cloud-init functionality with CloudStack datasource. Please " +
"refer documentation",
true, ConfigKey.Scope.Zone);
interface Topics {
String VM_POWER_STATE = "vm.powerstate";
}

View File

@ -49,15 +49,6 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException;
import javax.persistence.EntityExistsException;
import com.cloud.configuration.Resource;
import com.cloud.domain.Domain;
import com.cloud.domain.dao.DomainDao;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.user.dao.AccountDao;
import com.cloud.event.ActionEventUtils;
import com.google.gson.Gson;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
@ -160,6 +151,7 @@ import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.vo.DomainRouterJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.capacity.CapacityManager;
import com.cloud.configuration.Resource;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.ClusterDetailsVO;
import com.cloud.dc.ClusterVO;
@ -178,6 +170,9 @@ import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.DeploymentPlanningManager;
import com.cloud.deploy.DeploymentPlanningManagerImpl;
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
import com.cloud.domain.Domain;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEventUtils;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.event.UsageEventVO;
@ -189,6 +184,7 @@ import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.StorageAccessException;
import com.cloud.exception.StorageUnavailableException;
@ -211,6 +207,8 @@ import com.cloud.network.dao.NetworkDetailsDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.router.VirtualRouter;
import com.cloud.network.security.SecurityGroupManager;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.DiskOfferingInfo;
import com.cloud.offering.NetworkOffering;
@ -246,6 +244,7 @@ import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.ResourceLimitService;
import com.cloud.user.User;
import com.cloud.user.dao.AccountDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.DateUtil;
import com.cloud.utils.Journal;
@ -281,6 +280,7 @@ import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.snapshot.VMSnapshotManager;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import com.google.gson.Gson;
public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable {
@ -1101,6 +1101,19 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
markVolumesInPool(vm, answer);
}
protected void updateVmMetadataManufacturerAndProduct(VirtualMachineTO vmTO, VMInstanceVO vm) {
String metadataManufacturer = VmMetadataManufacturer.valueIn(vm.getDataCenterId());
if (StringUtils.isBlank(metadataManufacturer)) {
metadataManufacturer = VmMetadataManufacturer.defaultValue();
}
vmTO.setMetadataManufacturer(metadataManufacturer);
String metadataProduct = VmMetadataProductName.valueIn(vm.getDataCenterId());
if (StringUtils.isBlank(metadataManufacturer)) {
metadataProduct = String.format("CloudStack %s Hypervisor", vm.getHypervisorType().toString());
}
vmTO.setMetadataProductName(metadataProduct);
}
@Override
public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner)
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
@ -1259,6 +1272,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx);
final VirtualMachineTO vmTO = hvGuru.implement(vmProfile);
updateVmMetadataManufacturerAndProduct(vmTO, vm);
checkAndSetEnterSetupMode(vmTO, params);
@ -4742,7 +4756,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
VmOpLockStateRetry, VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval,
VmConfigDriveLabel, VmConfigDriveOnPrimaryPool, VmConfigDriveForceHostCacheUse, VmConfigDriveUseHostCacheOnUnsupportedPool,
HaVmRestartHostUp, ResourceCountRunningVMsonly, AllowExposeHypervisorHostname, AllowExposeHypervisorHostnameAccountLevel, SystemVmRootDiskSize,
AllowExposeDomainInMetadata, MetadataCustomCloudName
AllowExposeDomainInMetadata, MetadataCustomCloudName, VmMetadataManufacturer, VmMetadataProductName
};
}

View File

@ -37,26 +37,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.commons.collections.MapUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -76,24 +68,34 @@ import com.cloud.agent.api.Command;
import com.cloud.agent.api.StopAnswer;
import com.cloud.agent.api.StopCommand;
import com.cloud.agent.api.routing.NetworkElementCommand;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.ClusterDetailsVO;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.Pod;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.deploy.DataCenterDeployment;
import com.cloud.deploy.DeployDestination;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.DeploymentPlanningManager;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.HypervisorGuruManager;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.offering.ServiceOffering;
import com.cloud.org.Cluster;
import com.cloud.service.ServiceOfferingVO;
@ -115,7 +117,9 @@ import com.cloud.storage.dao.VMTemplateZoneDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.Journal;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
@ -220,8 +224,12 @@ public class VirtualMachineManagerImplTest {
@Mock
protected StateMachine2<State, VirtualMachine.Event, VirtualMachine> _stateMachine;
private ConfigDepotImpl configDepotImpl;
private boolean updatedConfigKeyDepot = false;
@Before
public void setup() {
ReflectionTestUtils.getField(VirtualMachineManager.VmMetadataManufacturer, "s_depot");
virtualMachineManagerImpl.setHostAllocators(new ArrayList<>());
when(vmInstanceMock.getId()).thenReturn(vmInstanceVoMockId);
@ -251,6 +259,13 @@ public class VirtualMachineManagerImplTest {
virtualMachineManagerImpl.setStoragePoolAllocators(storagePoolAllocators);
}
@After
public void cleanup() {
if (updatedConfigKeyDepot) {
ReflectionTestUtils.setField(VirtualMachineManager.VmMetadataManufacturer, "s_depot", configDepotImpl);
}
}
@Test
public void testaddHostIpToCertDetailsIfConfigAllows() {
Host vmHost = mock(Host.class);
@ -1236,4 +1251,48 @@ public class VirtualMachineManagerImplTest {
assertFalse(result.get(1L));
assertTrue(result.get(2L));
}
private void overrideVmMetadataConfigValue(final String manufacturer, final String product) {
ConfigKey configKey = VirtualMachineManager.VmMetadataManufacturer;
this.configDepotImpl = (ConfigDepotImpl)ReflectionTestUtils.getField(configKey, "s_depot");
ConfigDepotImpl configDepot = Mockito.mock(ConfigDepotImpl.class);
ScopedConfigStorage storage = Mockito.mock(ScopedConfigStorage.class);
Mockito.when(storage.getConfigValue(Mockito.anyLong(), Mockito.eq(configKey))).thenReturn(manufacturer);
Mockito.when(storage.getConfigValue(Mockito.anyLong(), Mockito.eq(VirtualMachineManager.VmMetadataProductName)))
.thenReturn(product);
Mockito.when(configDepot.findScopedConfigStorage(configKey)).thenReturn(storage);
Mockito.when(configDepot.findScopedConfigStorage(VirtualMachineManager.VmMetadataProductName)).thenReturn(storage);
ReflectionTestUtils.setField(configKey, "s_depot", configDepot);
updatedConfigKeyDepot = true;
}
private Pair<VirtualMachineTO, VMInstanceVO> getDummyVmTOAndVm() {
VirtualMachineTO virtualMachineTO = new VirtualMachineTO(1L, "VM", VirtualMachine.Type.User, 1,
1000, 256, 512, VirtualMachineTemplate.BootloaderType.HVM, "OS",
false, false, "Pass");
VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
Mockito.when(vm.getDataCenterId()).thenReturn(1L);
return new Pair<>(virtualMachineTO, vm);
}
@Test
public void testUpdateVmMetadataManufacturerAndProductDefaultManufacturer() {
overrideVmMetadataConfigValue("", "");
Pair<VirtualMachineTO, VMInstanceVO> pair = getDummyVmTOAndVm();
VirtualMachineTO to = pair.first();
virtualMachineManagerImpl.updateVmMetadataManufacturerAndProduct(to, pair.second());
Assert.assertEquals(VirtualMachineManager.VmMetadataManufacturer.defaultValue(), to.getMetadataManufacturer());
}
@Test
public void testUpdateVmMetadataManufacturerAndProductCustomManufacturer() {
String manufacturer = UUID.randomUUID().toString();
String product = UUID.randomUUID().toString();
overrideVmMetadataConfigValue(manufacturer, product);
Pair<VirtualMachineTO, VMInstanceVO> pair = getDummyVmTOAndVm();
VirtualMachineTO to = pair.first();
virtualMachineManagerImpl.updateVmMetadataManufacturerAndProduct(to, pair.second());
Assert.assertEquals(manufacturer, to.getMetadataManufacturer());
Assert.assertEquals(product, to.getMetadataProductName());
}
}

View File

@ -2838,6 +2838,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
GuestDef guest = new GuestDef();
configureGuestAndVMHypervisorType(vmTO, vm, guest);
guest.setManufacturer(vmTO.getMetadataManufacturer());
guest.setProduct(vmTO.getMetadataProductName());
guest.setGuestArch(guestCpuArch != null ? guestCpuArch : vmTO.getArch());
guest.setMachineType(isGuestAarch64() ? VIRT : PC);
guest.setBootType(GuestDef.BootType.BIOS);

View File

@ -95,6 +95,8 @@ public class LibvirtVMDef {
}
private GuestType _type;
private String manufacturer;
private String product;
private BootType _boottype;
private BootMode _bootmode;
private String _arch;
@ -124,6 +126,22 @@ public class LibvirtVMDef {
return _type;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public void setNvram(String nvram) { _nvram = nvram; }
public void setNvramTemplate(String nvramTemplate) { _nvramTemplate = nvramTemplate; }
@ -182,8 +200,8 @@ public class LibvirtVMDef {
guestDef.append("<sysinfo type='smbios'>\n");
guestDef.append("<system>\n");
guestDef.append("<entry name='manufacturer'>Apache Software Foundation</entry>\n");
guestDef.append("<entry name='product'>CloudStack " + _type.toString() + " Hypervisor</entry>\n");
guestDef.append("<entry name='manufacturer'>" + getManufacturer() +"</entry>\n");
guestDef.append("<entry name='product'>" + getProduct() + "</entry>\n");
guestDef.append("<entry name='uuid'>" + _uuid + "</entry>\n");
guestDef.append("</system>\n");
guestDef.append("</sysinfo>\n");