Skip to content

Commit 1e12a80

Browse files
authored
orchestration,hypervisor: allow custom manufacture, product for vm (#9163)
Signed-off-by: Abhishek Kumar <[email protected]>
1 parent b215abc commit 1e12a80

File tree

6 files changed

+149
-24
lines changed

6 files changed

+149
-24
lines changed

api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java

+18
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ public class VirtualMachineTO {
8484
Map<String, String> extraConfig = new HashMap<>();
8585
Map<Long, String> networkIdToNetworkNameMap = new HashMap<>();
8686
DeployAsIsInfoTO deployAsIsInfo;
87+
String metadataManufacturer;
88+
String metadataProductName;
8789

8890
public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader,
8991
String os, boolean enableHA, boolean limitCpuUse, String vncPassword) {
@@ -429,6 +431,22 @@ public void setDeployAsIsInfo(DeployAsIsInfoTO deployAsIsInfo) {
429431
this.deployAsIsInfo = deployAsIsInfo;
430432
}
431433

434+
public String getMetadataManufacturer() {
435+
return metadataManufacturer;
436+
}
437+
438+
public void setMetadataManufacturer(String metadataManufacturer) {
439+
this.metadataManufacturer = metadataManufacturer;
440+
}
441+
442+
public String getMetadataProductName() {
443+
return metadataProductName;
444+
}
445+
446+
public void setMetadataProductName(String metadataProductName) {
447+
this.metadataProductName = metadataProductName;
448+
}
449+
432450
@Override
433451
public String toString() {
434452
return String.format("VM {id: \"%s\", name: \"%s\", uuid: \"%s\", type: \"%s\"}", id, name, uuid, type);

engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java

+14
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,20 @@ public interface VirtualMachineManager extends Manager {
8787
ConfigKey<String> MetadataCustomCloudName = new ConfigKey<>("Advanced", String.class, "metadata.custom.cloud.name", "",
8888
"If provided, a custom cloud-name in cloud-init metadata", true, ConfigKey.Scope.Zone);
8989

90+
ConfigKey<String> VmMetadataManufacturer = new ConfigKey<>("Advanced", String.class,
91+
"vm.metadata.manufacturer", "Apache Software Foundation",
92+
"If provided, a custom manufacturer will be used in the instance metadata. When an empty" +
93+
"value is set then default manufacturer will be 'Apache Software Foundation'. " +
94+
"A custom manufacturer may break cloud-init functionality with CloudStack datasource. Please " +
95+
"refer documentation", true, ConfigKey.Scope.Zone);
96+
ConfigKey<String> VmMetadataProductName = new ConfigKey<>("Advanced", String.class,
97+
"vm.metadata.product", "",
98+
"If provided, a custom product name will be used in the instance metadata. When an empty" +
99+
"value is set then default product name will be 'CloudStack <HYPERVISIOR_NAME> Hypervisor'. " +
100+
"A custom product name may break cloud-init functionality with CloudStack datasource. Please " +
101+
"refer documentation",
102+
true, ConfigKey.Scope.Zone);
103+
90104
interface Topics {
91105
String VM_POWER_STATE = "vm.powerstate";
92106
}

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

+24-10
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,6 @@
4949
import javax.naming.ConfigurationException;
5050
import javax.persistence.EntityExistsException;
5151

52-
import com.cloud.configuration.Resource;
53-
import com.cloud.domain.Domain;
54-
import com.cloud.domain.dao.DomainDao;
55-
import com.cloud.exception.ResourceAllocationException;
56-
import com.cloud.network.vpc.VpcVO;
57-
import com.cloud.network.vpc.dao.VpcDao;
58-
import com.cloud.user.dao.AccountDao;
59-
import com.cloud.event.ActionEventUtils;
60-
import com.google.gson.Gson;
6152
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
6253
import org.apache.cloudstack.annotation.AnnotationService;
6354
import org.apache.cloudstack.annotation.dao.AnnotationDao;
@@ -160,6 +151,7 @@
160151
import com.cloud.api.query.vo.DomainRouterJoinVO;
161152
import com.cloud.api.query.vo.UserVmJoinVO;
162153
import com.cloud.capacity.CapacityManager;
154+
import com.cloud.configuration.Resource;
163155
import com.cloud.dc.ClusterDetailsDao;
164156
import com.cloud.dc.ClusterDetailsVO;
165157
import com.cloud.dc.ClusterVO;
@@ -178,6 +170,9 @@
178170
import com.cloud.deploy.DeploymentPlanningManager;
179171
import com.cloud.deploy.DeploymentPlanningManagerImpl;
180172
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
173+
import com.cloud.domain.Domain;
174+
import com.cloud.domain.dao.DomainDao;
175+
import com.cloud.event.ActionEventUtils;
181176
import com.cloud.event.EventTypes;
182177
import com.cloud.event.UsageEventUtils;
183178
import com.cloud.event.UsageEventVO;
@@ -189,6 +184,7 @@
189184
import com.cloud.exception.InsufficientServerCapacityException;
190185
import com.cloud.exception.InvalidParameterValueException;
191186
import com.cloud.exception.OperationTimedoutException;
187+
import com.cloud.exception.ResourceAllocationException;
192188
import com.cloud.exception.ResourceUnavailableException;
193189
import com.cloud.exception.StorageAccessException;
194190
import com.cloud.exception.StorageUnavailableException;
@@ -211,6 +207,8 @@
211207
import com.cloud.network.dao.NetworkVO;
212208
import com.cloud.network.router.VirtualRouter;
213209
import com.cloud.network.security.SecurityGroupManager;
210+
import com.cloud.network.vpc.VpcVO;
211+
import com.cloud.network.vpc.dao.VpcDao;
214212
import com.cloud.offering.DiskOffering;
215213
import com.cloud.offering.DiskOfferingInfo;
216214
import com.cloud.offering.NetworkOffering;
@@ -246,6 +244,7 @@
246244
import com.cloud.user.Account;
247245
import com.cloud.user.ResourceLimitService;
248246
import com.cloud.user.User;
247+
import com.cloud.user.dao.AccountDao;
249248
import com.cloud.uservm.UserVm;
250249
import com.cloud.utils.DateUtil;
251250
import com.cloud.utils.Journal;
@@ -281,6 +280,7 @@
281280
import com.cloud.vm.snapshot.VMSnapshotManager;
282281
import com.cloud.vm.snapshot.VMSnapshotVO;
283282
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
283+
import com.google.gson.Gson;
284284

285285
public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable {
286286

@@ -1101,6 +1101,19 @@ protected void checkAndAttemptMigrateVmAcrossCluster(final VMInstanceVO vm, fina
11011101
markVolumesInPool(vm, answer);
11021102
}
11031103

1104+
protected void updateVmMetadataManufacturerAndProduct(VirtualMachineTO vmTO, VMInstanceVO vm) {
1105+
String metadataManufacturer = VmMetadataManufacturer.valueIn(vm.getDataCenterId());
1106+
if (StringUtils.isBlank(metadataManufacturer)) {
1107+
metadataManufacturer = VmMetadataManufacturer.defaultValue();
1108+
}
1109+
vmTO.setMetadataManufacturer(metadataManufacturer);
1110+
String metadataProduct = VmMetadataProductName.valueIn(vm.getDataCenterId());
1111+
if (StringUtils.isBlank(metadataManufacturer)) {
1112+
metadataProduct = String.format("CloudStack %s Hypervisor", vm.getHypervisorType().toString());
1113+
}
1114+
vmTO.setMetadataProductName(metadataProduct);
1115+
}
1116+
11041117
@Override
11051118
public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner)
11061119
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
@@ -1259,6 +1272,7 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
12591272
vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx);
12601273

12611274
final VirtualMachineTO vmTO = hvGuru.implement(vmProfile);
1275+
updateVmMetadataManufacturerAndProduct(vmTO, vm);
12621276

12631277
checkAndSetEnterSetupMode(vmTO, params);
12641278

@@ -4742,7 +4756,7 @@ public ConfigKey<?>[] getConfigKeys() {
47424756
VmOpLockStateRetry, VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval,
47434757
VmConfigDriveLabel, VmConfigDriveOnPrimaryPool, VmConfigDriveForceHostCacheUse, VmConfigDriveUseHostCacheOnUnsupportedPool,
47444758
HaVmRestartHostUp, ResourceCountRunningVMsonly, AllowExposeHypervisorHostname, AllowExposeHypervisorHostnameAccountLevel, SystemVmRootDiskSize,
4745-
AllowExposeDomainInMetadata, MetadataCustomCloudName
4759+
AllowExposeDomainInMetadata, MetadataCustomCloudName, VmMetadataManufacturer, VmMetadataProductName
47464760
};
47474761
}
47484762

engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java

+71-12
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,18 @@
3737
import java.util.List;
3838
import java.util.Map;
3939
import java.util.Random;
40+
import java.util.UUID;
4041
import java.util.stream.Collectors;
4142

42-
import com.cloud.agent.api.to.VirtualMachineTO;
43-
import com.cloud.api.query.vo.UserVmJoinVO;
44-
import com.cloud.dc.DataCenterVO;
45-
import com.cloud.dc.dao.DataCenterDao;
46-
import com.cloud.domain.DomainVO;
47-
import com.cloud.domain.dao.DomainDao;
48-
import com.cloud.network.dao.NetworkDao;
49-
import com.cloud.network.dao.NetworkVO;
50-
import com.cloud.network.vpc.VpcVO;
51-
import com.cloud.network.vpc.dao.VpcDao;
52-
import com.cloud.user.AccountVO;
53-
import com.cloud.user.dao.AccountDao;
5443
import org.apache.cloudstack.context.CallContext;
5544
import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
5645
import org.apache.cloudstack.framework.config.ConfigKey;
46+
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
47+
import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
5748
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
5849
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
5950
import org.apache.commons.collections.MapUtils;
51+
import org.junit.After;
6052
import org.junit.Assert;
6153
import org.junit.Before;
6254
import org.junit.Test;
@@ -76,24 +68,34 @@
7668
import com.cloud.agent.api.StopAnswer;
7769
import com.cloud.agent.api.StopCommand;
7870
import com.cloud.agent.api.routing.NetworkElementCommand;
71+
import com.cloud.agent.api.to.VirtualMachineTO;
7972
import com.cloud.api.query.dao.UserVmJoinDao;
73+
import com.cloud.api.query.vo.UserVmJoinVO;
8074
import com.cloud.dc.ClusterDetailsDao;
8175
import com.cloud.dc.ClusterDetailsVO;
8276
import com.cloud.dc.ClusterVO;
77+
import com.cloud.dc.DataCenterVO;
8378
import com.cloud.dc.Pod;
8479
import com.cloud.dc.dao.ClusterDao;
80+
import com.cloud.dc.dao.DataCenterDao;
8581
import com.cloud.deploy.DataCenterDeployment;
8682
import com.cloud.deploy.DeployDestination;
8783
import com.cloud.deploy.DeploymentPlan;
8884
import com.cloud.deploy.DeploymentPlanner;
8985
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
9086
import com.cloud.deploy.DeploymentPlanningManager;
87+
import com.cloud.domain.DomainVO;
88+
import com.cloud.domain.dao.DomainDao;
9189
import com.cloud.exception.InvalidParameterValueException;
9290
import com.cloud.host.Host;
9391
import com.cloud.host.HostVO;
9492
import com.cloud.host.dao.HostDao;
9593
import com.cloud.hypervisor.Hypervisor.HypervisorType;
9694
import com.cloud.hypervisor.HypervisorGuruManager;
95+
import com.cloud.network.dao.NetworkDao;
96+
import com.cloud.network.dao.NetworkVO;
97+
import com.cloud.network.vpc.VpcVO;
98+
import com.cloud.network.vpc.dao.VpcDao;
9799
import com.cloud.offering.ServiceOffering;
98100
import com.cloud.org.Cluster;
99101
import com.cloud.service.ServiceOfferingVO;
@@ -115,7 +117,9 @@
115117
import com.cloud.storage.dao.VolumeDao;
116118
import com.cloud.template.VirtualMachineTemplate;
117119
import com.cloud.user.Account;
120+
import com.cloud.user.AccountVO;
118121
import com.cloud.user.User;
122+
import com.cloud.user.dao.AccountDao;
119123
import com.cloud.utils.Journal;
120124
import com.cloud.utils.Pair;
121125
import com.cloud.utils.Ternary;
@@ -220,8 +224,12 @@ public class VirtualMachineManagerImplTest {
220224
@Mock
221225
protected StateMachine2<State, VirtualMachine.Event, VirtualMachine> _stateMachine;
222226

227+
private ConfigDepotImpl configDepotImpl;
228+
private boolean updatedConfigKeyDepot = false;
229+
223230
@Before
224231
public void setup() {
232+
ReflectionTestUtils.getField(VirtualMachineManager.VmMetadataManufacturer, "s_depot");
225233
virtualMachineManagerImpl.setHostAllocators(new ArrayList<>());
226234

227235
when(vmInstanceMock.getId()).thenReturn(vmInstanceVoMockId);
@@ -251,6 +259,13 @@ public void setup() {
251259
virtualMachineManagerImpl.setStoragePoolAllocators(storagePoolAllocators);
252260
}
253261

262+
@After
263+
public void cleanup() {
264+
if (updatedConfigKeyDepot) {
265+
ReflectionTestUtils.setField(VirtualMachineManager.VmMetadataManufacturer, "s_depot", configDepotImpl);
266+
}
267+
}
268+
254269
@Test
255270
public void testaddHostIpToCertDetailsIfConfigAllows() {
256271
Host vmHost = mock(Host.class);
@@ -1236,4 +1251,48 @@ public void testGetDiskOfferingSuitabilityForVm() {
12361251
assertFalse(result.get(1L));
12371252
assertTrue(result.get(2L));
12381253
}
1254+
1255+
private void overrideVmMetadataConfigValue(final String manufacturer, final String product) {
1256+
ConfigKey configKey = VirtualMachineManager.VmMetadataManufacturer;
1257+
this.configDepotImpl = (ConfigDepotImpl)ReflectionTestUtils.getField(configKey, "s_depot");
1258+
ConfigDepotImpl configDepot = Mockito.mock(ConfigDepotImpl.class);
1259+
ScopedConfigStorage storage = Mockito.mock(ScopedConfigStorage.class);
1260+
Mockito.when(storage.getConfigValue(Mockito.anyLong(), Mockito.eq(configKey))).thenReturn(manufacturer);
1261+
Mockito.when(storage.getConfigValue(Mockito.anyLong(), Mockito.eq(VirtualMachineManager.VmMetadataProductName)))
1262+
.thenReturn(product);
1263+
Mockito.when(configDepot.findScopedConfigStorage(configKey)).thenReturn(storage);
1264+
Mockito.when(configDepot.findScopedConfigStorage(VirtualMachineManager.VmMetadataProductName)).thenReturn(storage);
1265+
ReflectionTestUtils.setField(configKey, "s_depot", configDepot);
1266+
updatedConfigKeyDepot = true;
1267+
}
1268+
1269+
private Pair<VirtualMachineTO, VMInstanceVO> getDummyVmTOAndVm() {
1270+
VirtualMachineTO virtualMachineTO = new VirtualMachineTO(1L, "VM", VirtualMachine.Type.User, 1,
1271+
1000, 256, 512, VirtualMachineTemplate.BootloaderType.HVM, "OS",
1272+
false, false, "Pass");
1273+
VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
1274+
Mockito.when(vm.getDataCenterId()).thenReturn(1L);
1275+
return new Pair<>(virtualMachineTO, vm);
1276+
}
1277+
1278+
@Test
1279+
public void testUpdateVmMetadataManufacturerAndProductDefaultManufacturer() {
1280+
overrideVmMetadataConfigValue("", "");
1281+
Pair<VirtualMachineTO, VMInstanceVO> pair = getDummyVmTOAndVm();
1282+
VirtualMachineTO to = pair.first();
1283+
virtualMachineManagerImpl.updateVmMetadataManufacturerAndProduct(to, pair.second());
1284+
Assert.assertEquals(VirtualMachineManager.VmMetadataManufacturer.defaultValue(), to.getMetadataManufacturer());
1285+
}
1286+
1287+
@Test
1288+
public void testUpdateVmMetadataManufacturerAndProductCustomManufacturer() {
1289+
String manufacturer = UUID.randomUUID().toString();
1290+
String product = UUID.randomUUID().toString();
1291+
overrideVmMetadataConfigValue(manufacturer, product);
1292+
Pair<VirtualMachineTO, VMInstanceVO> pair = getDummyVmTOAndVm();
1293+
VirtualMachineTO to = pair.first();
1294+
virtualMachineManagerImpl.updateVmMetadataManufacturerAndProduct(to, pair.second());
1295+
Assert.assertEquals(manufacturer, to.getMetadataManufacturer());
1296+
Assert.assertEquals(product, to.getMetadataProductName());
1297+
}
12391298
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

+2
Original file line numberDiff line numberDiff line change
@@ -2838,6 +2838,8 @@ protected GuestDef createGuestFromSpec(VirtualMachineTO vmTO, LibvirtVMDef vm, S
28382838
GuestDef guest = new GuestDef();
28392839

28402840
configureGuestAndVMHypervisorType(vmTO, vm, guest);
2841+
guest.setManufacturer(vmTO.getMetadataManufacturer());
2842+
guest.setProduct(vmTO.getMetadataProductName());
28412843
guest.setGuestArch(guestCpuArch != null ? guestCpuArch : vmTO.getArch());
28422844
guest.setMachineType(isGuestAarch64() ? VIRT : PC);
28432845
guest.setBootType(GuestDef.BootType.BIOS);

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ public String toString() {
9595
}
9696

9797
private GuestType _type;
98+
private String manufacturer;
99+
private String product;
98100
private BootType _boottype;
99101
private BootMode _bootmode;
100102
private String _arch;
@@ -124,6 +126,22 @@ public GuestType getGuestType() {
124126
return _type;
125127
}
126128

129+
public String getManufacturer() {
130+
return manufacturer;
131+
}
132+
133+
public void setManufacturer(String manufacturer) {
134+
this.manufacturer = manufacturer;
135+
}
136+
137+
public String getProduct() {
138+
return product;
139+
}
140+
141+
public void setProduct(String product) {
142+
this.product = product;
143+
}
144+
127145
public void setNvram(String nvram) { _nvram = nvram; }
128146

129147
public void setNvramTemplate(String nvramTemplate) { _nvramTemplate = nvramTemplate; }
@@ -182,8 +200,8 @@ public String toString() {
182200

183201
guestDef.append("<sysinfo type='smbios'>\n");
184202
guestDef.append("<system>\n");
185-
guestDef.append("<entry name='manufacturer'>Apache Software Foundation</entry>\n");
186-
guestDef.append("<entry name='product'>CloudStack " + _type.toString() + " Hypervisor</entry>\n");
203+
guestDef.append("<entry name='manufacturer'>" + getManufacturer() +"</entry>\n");
204+
guestDef.append("<entry name='product'>" + getProduct() + "</entry>\n");
187205
guestDef.append("<entry name='uuid'>" + _uuid + "</entry>\n");
188206
guestDef.append("</system>\n");
189207
guestDef.append("</sysinfo>\n");

0 commit comments

Comments
 (0)