Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -429,6 +431,22 @@ public void setDeployAsIsInfo(DeployAsIsInfoTO deployAsIsInfo) {
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);
Expand Down
14 changes: 14 additions & 0 deletions engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,6 @@
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;
Expand Down Expand Up @@ -160,6 +151,7 @@
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;
Expand All @@ -178,6 +170,9 @@
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;
Expand All @@ -189,6 +184,7 @@
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;
Expand All @@ -211,6 +207,8 @@
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;
Expand Down Expand Up @@ -246,6 +244,7 @@
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;
Expand Down Expand Up @@ -281,6 +280,7 @@
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 {

Expand Down Expand Up @@ -1101,6 +1101,19 @@ protected void checkAndAttemptMigrateVmAcrossCluster(final VMInstanceVO vm, fina
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 {
Expand Down Expand Up @@ -1259,6 +1272,7 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx);

final VirtualMachineTO vmTO = hvGuru.implement(vmProfile);
updateVmMetadataManufacturerAndProduct(vmTO, vm);

checkAndSetEnterSetupMode(vmTO, params);

Expand Down Expand Up @@ -4742,7 +4756,7 @@ public ConfigKey<?>[] getConfigKeys() {
VmOpLockStateRetry, VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval,
VmConfigDriveLabel, VmConfigDriveOnPrimaryPool, VmConfigDriveForceHostCacheUse, VmConfigDriveUseHostCacheOnUnsupportedPool,
HaVmRestartHostUp, ResourceCountRunningVMsonly, AllowExposeHypervisorHostname, AllowExposeHypervisorHostnameAccountLevel, SystemVmRootDiskSize,
AllowExposeDomainInMetadata, MetadataCustomCloudName
AllowExposeDomainInMetadata, MetadataCustomCloudName, VmMetadataManufacturer, VmMetadataProductName
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,18 @@
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;
Expand All @@ -76,24 +68,34 @@
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;
Expand All @@ -115,7 +117,9 @@
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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -251,6 +259,13 @@ public void setup() {
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);
Expand Down Expand Up @@ -1236,4 +1251,48 @@ public void testGetDiskOfferingSuitabilityForVm() {
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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2838,6 +2838,8 @@ protected GuestDef createGuestFromSpec(VirtualMachineTO vmTO, LibvirtVMDef vm, S
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ public String toString() {
}

private GuestType _type;
private String manufacturer;
private String product;
private BootType _boottype;
private BootMode _bootmode;
private String _arch;
Expand Down Expand Up @@ -124,6 +126,22 @@ public GuestType getGuestType() {
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; }
Expand Down Expand Up @@ -182,8 +200,8 @@ public String toString() {

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");
Expand Down