Skip to content

Commit bc7ec16

Browse files
committed
fix for sshkeypair and guest os restore
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent 721dbea commit bc7ec16

8 files changed

Lines changed: 220 additions & 80 deletions

File tree

api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme
160160
private String sshKeyPairName;
161161

162162
@Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "names of the ssh key pairs used to login to the virtual machine")
163-
private List<String> sshKeyPairNames;
163+
protected List<String> sshKeyPairNames;
164164

165165
@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only")
166166
private Long hostId;

api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ public void setSnapshotId(Long snapshotId) {
187187
this.snapshotId = snapshotId;
188188
}
189189

190+
public void setSshKeyPairNames(List<String> sshKeyPairNames) {
191+
this.sshKeyPairNames = sshKeyPairNames;
192+
}
193+
190194
@Override
191195
public void execute() {
192196
UserVm result;

plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,13 @@
170170
import com.cloud.server.TaggedResourceService;
171171
import com.cloud.service.ServiceOfferingVO;
172172
import com.cloud.service.dao.ServiceOfferingDao;
173+
import com.cloud.storage.GuestOS;
173174
import com.cloud.storage.Storage;
174175
import com.cloud.storage.VMTemplateVO;
175176
import com.cloud.storage.Volume;
176177
import com.cloud.storage.VolumeApiService;
177178
import com.cloud.storage.VolumeVO;
179+
import com.cloud.storage.dao.GuestOSDao;
178180
import com.cloud.storage.dao.VMTemplateDao;
179181
import com.cloud.storage.dao.VolumeDao;
180182
import com.cloud.storage.dao.VolumeDetailsDao;
@@ -183,8 +185,10 @@
183185
import com.cloud.user.Account;
184186
import com.cloud.user.AccountService;
185187
import com.cloud.user.DomainService;
188+
import com.cloud.user.SSHKeyPairVO;
186189
import com.cloud.user.User;
187190
import com.cloud.user.UserDataVO;
191+
import com.cloud.user.dao.SSHKeyPairDao;
188192
import com.cloud.user.dao.UserDataDao;
189193
import com.cloud.uservm.UserVm;
190194
import com.cloud.utils.EnumUtils;
@@ -215,6 +219,7 @@ public class ServerAdapter extends ManagerBase {
215219
);
216220
private static final String VM_TAG_KEY = "veeam_tag";
217221
private static final String WORKER_VM_GUEST_CPU_MODE = "host-passthrough";
222+
private static final String WORKER_VM_GUEST_OS = "AlmaLinux 9";
218223
private static final String RESTORE_CONFIG = "restore.config";
219224

220225
@Inject
@@ -322,6 +327,12 @@ public class ServerAdapter extends ManagerBase {
322327
@Inject
323328
TaggedResourceService taggedResourceService;
324329

330+
@Inject
331+
SSHKeyPairDao sshKeyPairDao;
332+
333+
@Inject
334+
GuestOSDao guestOSDao;
335+
325336
protected static Map<String, Tag> getDummyTags() {
326337
Map<String, Tag> tags = new HashMap<>();
327338
Tag rootTag = ResourceTagVOToTagConverter.getRootTag();
@@ -512,22 +523,70 @@ protected ServiceOffering getServiceOfferingIdForVmCreation(com.cloud.dc.DataCen
512523
return offering;
513524
}
514525

515-
protected VMTemplateVO getTemplateForInstanceCreation(String templateUuid) {
526+
protected GuestOS getGuestOsForInstance(Vm request, boolean isWorkerVm) {
527+
if (isWorkerVm) {
528+
GuestOS os = guestOSDao.findOneByDisplayName(WORKER_VM_GUEST_OS);
529+
if (os == null) {
530+
logger.warn("Guest OS with name {} for worker VM not found, VM will be created with default guest OS",
531+
WORKER_VM_GUEST_OS);
532+
}
533+
return os;
534+
}
535+
final String guestOsId = request.getGuestOsId();
536+
final String guestOsName = request.getGuestOsName();
537+
if (StringUtils.isAllBlank(guestOsId, guestOsName)) {
538+
return null;
539+
}
540+
GuestOS os = null;
541+
if (StringUtils.isNotBlank(guestOsId)) {
542+
os = guestOSDao.findByUuid(guestOsId);
543+
}
544+
if (os == null && StringUtils.isNotBlank(guestOsName)) {
545+
os = guestOSDao.findOneByDisplayName(guestOsName);
546+
}
547+
if (os == null) {
548+
logger.debug("Guest OS could not be identified with ID: {} and name: {} for the VM request", os);
549+
}
550+
return os;
551+
}
552+
553+
protected VMTemplateVO getTemplateForInstanceCreation(String templateUuid, GuestOS guestOs) {
516554
if (StringUtils.isBlank(templateUuid)) {
517555
return null;
518556
}
519557
VMTemplateVO template = templateDao.findByUuid(templateUuid);
520558
if (template == null) {
521-
logger.warn("Template with ID {} not found, VM will be created with default template", templateUuid);
559+
logger.warn("Template with ID {} not found, guest OS: {}, VM will be created with default template",
560+
guestOs, templateUuid);
522561
return null;
523562
}
524563
return template;
525564
}
526565

566+
protected List<String> getValidatedSshKeyPairNames(String sshKeyPairNames, Account owner) {
567+
if (StringUtils.isBlank(sshKeyPairNames)) {
568+
return null;
569+
}
570+
List<String> sshKeys = Arrays.stream(sshKeyPairNames.split(","))
571+
.map(String::trim)
572+
.filter(StringUtils::isNotEmpty)
573+
.collect(Collectors.toList());
574+
Account account = owner != null ? owner : CallContext.current().getCallingAccount();
575+
List<SSHKeyPairVO> keyPairs = sshKeyPairDao.findByNames(account.getId(), account.getDomainId(), sshKeys);
576+
List<String> validatedNames = keyPairs.stream().map(SSHKeyPairVO::getName).collect(Collectors.toList());
577+
if (sshKeys.size() != validatedNames.size()) {
578+
logger.warn("Some SSH key pairs specified in the VM creation request were not found for {}. " +
579+
"Specified SSH key pairs: [{}], valid SSH key pairs: [{}]",
580+
account, StringUtils.join(sshKeys, ","), StringUtils.join(validatedNames, ","));
581+
}
582+
return validatedNames;
583+
}
584+
527585
protected Pair<Vm, UserVm> createInstance(com.cloud.dc.DataCenter zone, Long clusterId, Account owner, Long domainId,
528586
String accountName, Long projectId, String name, String displayName, String serviceOfferingUuid,
529-
int cpu, int memory, String templateUuid, String userdata, ApiConstants.BootType bootType,
530-
ApiConstants.BootMode bootMode, String affinityGroupId, String userDataId, Map<String, String> details) {
587+
int cpu, int memory, String templateUuid, GuestOS guestOs, String userdata, ApiConstants.BootType bootType,
588+
ApiConstants.BootMode bootMode, String affinityGroupId, String userDataId, String sshKeyPairNames,
589+
Map<String, String> details) {
531590
Account account = owner != null ? owner : CallContext.current().getCallingAccount();
532591
ServiceOffering serviceOffering = getServiceOfferingIdForVmCreation(zone, account, serviceOfferingUuid, cpu,
533592
memory);
@@ -560,9 +619,11 @@ protected Pair<Vm, UserVm> createInstance(com.cloud.dc.DataCenter zone, Long clu
560619
if (bootMode != null) {
561620
cmd.setBootMode(bootMode.toString());
562621
}
563-
VMTemplateVO template = getTemplateForInstanceCreation(templateUuid);
622+
VMTemplateVO template = getTemplateForInstanceCreation(templateUuid, guestOs);
564623
if (template != null) {
565624
cmd.setTemplateId(template.getId());
625+
} else if (guestOs != null) {
626+
CallContext.current().putContextParameter(ApiConstants.OS_ID, guestOs);
566627
}
567628
if (StringUtils.isNotBlank(affinityGroupId)) {
568629
AffinityGroupVO group = affinityGroupDao.findByUuid(affinityGroupId);
@@ -582,6 +643,9 @@ protected Pair<Vm, UserVm> createInstance(com.cloud.dc.DataCenter zone, Long clu
582643
cmd.setUserDataId(userData.getId());
583644
}
584645
}
646+
if (StringUtils.isNotBlank(sshKeyPairNames)) {
647+
cmd.setSshKeyPairNames(getValidatedSshKeyPairNames(sshKeyPairNames, owner));
648+
}
585649
cmd.setHypervisor(Hypervisor.HypervisorType.KVM.name());
586650
Map<String, String> instanceDetails = getDetailsForInstanceCreation(userdata, serviceOffering, details);
587651
if (MapUtils.isNotEmpty(instanceDetails)) {
@@ -1039,10 +1103,11 @@ public Vm createInstance(Vm request) {
10391103
if (request.getTemplate() != null && StringUtils.isNotEmpty(request.getTemplate().getId())) {
10401104
templateUuid = request.getTemplate().getId();
10411105
}
1106+
GuestOS guestOs = getGuestOsForInstance(request, StringUtils.isNotEmpty(userdata));
10421107
Pair<Vm, UserVm> result = createInstance(zone, clusterId, owner, ownerDetails.first(), ownerDetails.second(),
1043-
ownerDetails.third(), name, displayName, serviceOfferingUuid, cpu, memoryMB, templateUuid,
1108+
ownerDetails.third(), name, displayName, serviceOfferingUuid, cpu, memoryMB, templateUuid, guestOs,
10441109
userdata, bootOptions.first(), bootOptions.second(), request.getAffinityGroupId(),
1045-
request.getUserDataId(), request.getDetails());
1110+
request.getUserDataId(), request.getSshKeyPairNames(), request.getDetails());
10461111
saveInstanceRestoreConfig(request, result.second());
10471112
return result.first();
10481113
}
@@ -1258,7 +1323,7 @@ public DiskAttachment attachInstanceDisk(final String vmUuid, final DiskAttachme
12581323
}
12591324
}
12601325
Long deviceId = null;
1261-
if (Volume.Type.ROOT.equals(volumeVO.getVolumeType())) {
1326+
if (Boolean.parseBoolean(request.getBootable()) || Volume.Type.ROOT.equals(volumeVO.getVolumeType())) {
12621327
deviceId = 0L;
12631328
}
12641329
Volume volume = volumeApiService.attachVolumeToVM(vmVo.getId(), volumeVO.getId(), deviceId, false);

plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ public static Vm toVm(final UserVmJoinVO src,
170170
dst.setAffinityGroupId(src.getAffinityGroupUuid());
171171
dst.setUserDataId(src.getUserDataUuid());
172172
dst.setDetails(details);
173+
dst.setSshKeyPairNames(src.getKeypairNames());
174+
dst.setGuestOsId(src.getGuestOsUuid());
175+
dst.setGuestOsName(src.getGuestOsDisplayName());
173176

174177
// Keep at last
175178
if (allContent) {

plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/OvfXmlUtil.java

Lines changed: 88 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -215,69 +215,7 @@ public static String toXml(final Vm vm, final UserVmJoinVO vo) {
215215
}
216216
sb.append("</Section>");
217217

218-
if (vo != null) {
219-
// -- Add a section for CloudStack-specific metadata that some consumers might look for (e.g. for import back into CloudStack) ---
220-
// Add CloudStack-specific metadata section
221-
sb.append("<Section xsi:type=\"ovf:CloudStackMetadata_Type\">");
222-
sb.append("<Info>CloudStack specific metadata</Info>");
223-
sb.append("<CloudStack>");
224-
sb.append("<AccountId>").append(vo.getAccountUuid()).append("</AccountId>");
225-
sb.append("<DomainId>").append(vo.getDomainUuid()).append("</DomainId>");
226-
sb.append("<ProjectId>").append(escapeText(vo.getProjectUuid())).append("</ProjectId>");
227-
if (vm.getCpuProfile() != null && StringUtils.isNotBlank(vm.getCpuProfile().getId())) {
228-
sb.append("<ServiceOfferingId>").append(vm.getCpuProfile().getId()).append("</ServiceOfferingId>");
229-
}
230-
sb.append("<DataDiskOfferingIdMap>");
231-
for (DiskAttachment da : diskAttachments(vm)) {
232-
if (da == null || da.getDisk() == null || StringUtils.isBlank(da.getDisk().getId())) {
233-
continue;
234-
}
235-
final Disk d = da.getDisk();
236-
sb.append("<Entry>");
237-
sb.append("<DiskId>").append(escapeText(d.getId())).append("</DiskId>");
238-
sb.append("<OfferingId>").append(d.getDiskProfile().getId()).append("</OfferingId>");
239-
sb.append("</Entry>");
240-
}
241-
sb.append("</DataDiskOfferingIdMap>");
242-
if (MapUtils.isNotEmpty(vm.getDetails())) {
243-
sb.append("<Details>");
244-
for (Map.Entry<String, String> entry : vm.getDetails().entrySet()) {
245-
sb.append("<Detail>");
246-
sb.append("<Key>").append(escapeText(entry.getKey())).append("</Key>");
247-
sb.append("<Value>").append(escapeText(entry.getValue())).append("</Value>");
248-
sb.append("</Detail>");
249-
}
250-
sb.append("</Details>");
251-
}
252-
if (vo.getUserDataId() != null) {
253-
sb.append("<UserDataId>").append(escapeText(vo.getUserDataUuid())).append("</UserDataId>");
254-
}
255-
if (vo.getAffinityGroupId() != null) {
256-
sb.append("<AffinityGroupId>").append(escapeText(vo.getAffinityGroupUuid())).append("</AffinityGroupId>");
257-
}
258-
if (vm.getNics() != null && CollectionUtils.isNotEmpty(vm.getNics().getItems())) {
259-
sb.append("<Nics>");
260-
for (Nic nic : nics(vm)) {
261-
if (nic == null || StringUtils.isBlank(nic.getId())) {
262-
continue;
263-
}
264-
String networkId = nicNetworkId(nic);
265-
if (networkId == null) {
266-
continue;
267-
}
268-
sb.append("<Nic>");
269-
sb.append("<Id>").append(escapeText(nic.getId())).append("</Id>");
270-
sb.append("<NetworkId>").append(escapeText(networkId)).append("</NetworkId>");
271-
sb.append("<MACAddress>").append(escapeText(nicMac(nic))).append("</MACAddress>");
272-
sb.append("<Ip4Address>").append(escapeText(nicIp(nic, "v4"))).append("</Ip4Address>");
273-
sb.append("<Ip6Address>").append(escapeText(nicIp(nic, "v6"))).append("</Ip6Address>");
274-
sb.append("</Nic>");
275-
}
276-
sb.append("</Nics>");
277-
}
278-
sb.append("</CloudStack>");
279-
sb.append("</Section>");
280-
}
218+
addCloudStackMetadata(vm, vo, sb);
281219

282220
// --- Content / VirtualSystem ---
283221
sb.append("<Content ovf:id=\"out\" xsi:type=\"ovf:VirtualSystem_Type\">");
@@ -470,6 +408,81 @@ public static String toXml(final Vm vm, final UserVmJoinVO vo) {
470408
return sb.toString();
471409
}
472410

411+
private static void addCloudStackMetadata(Vm vm, UserVmJoinVO vo, StringBuilder sb) {
412+
if (vo != null) {
413+
// -- Add a section for CloudStack-specific metadata that some consumers might look for (e.g. for import back into CloudStack) ---
414+
// Add CloudStack-specific metadata section
415+
sb.append("<Section xsi:type=\"ovf:CloudStackMetadata_Type\">");
416+
sb.append("<Info>CloudStack specific metadata</Info>");
417+
sb.append("<CloudStack>");
418+
sb.append("<AccountId>").append(vo.getAccountUuid()).append("</AccountId>");
419+
sb.append("<DomainId>").append(vo.getDomainUuid()).append("</DomainId>");
420+
sb.append("<ProjectId>").append(escapeText(vo.getProjectUuid())).append("</ProjectId>");
421+
if (vm.getCpuProfile() != null && StringUtils.isNotBlank(vm.getCpuProfile().getId())) {
422+
sb.append("<ServiceOfferingId>").append(vm.getCpuProfile().getId()).append("</ServiceOfferingId>");
423+
}
424+
sb.append("<DataDiskOfferingIdMap>");
425+
for (DiskAttachment da : diskAttachments(vm)) {
426+
if (da == null || da.getDisk() == null || StringUtils.isBlank(da.getDisk().getId())) {
427+
continue;
428+
}
429+
final Disk d = da.getDisk();
430+
sb.append("<Entry>");
431+
sb.append("<DiskId>").append(escapeText(d.getId())).append("</DiskId>");
432+
sb.append("<OfferingId>").append(d.getDiskProfile().getId()).append("</OfferingId>");
433+
sb.append("</Entry>");
434+
}
435+
sb.append("</DataDiskOfferingIdMap>");
436+
if (MapUtils.isNotEmpty(vm.getDetails())) {
437+
sb.append("<Details>");
438+
for (Map.Entry<String, String> entry : vm.getDetails().entrySet()) {
439+
sb.append("<Detail>");
440+
sb.append("<Key>").append(escapeText(entry.getKey())).append("</Key>");
441+
sb.append("<Value>").append(escapeText(entry.getValue())).append("</Value>");
442+
sb.append("</Detail>");
443+
}
444+
sb.append("</Details>");
445+
}
446+
if (vo.getUserDataId() != null) {
447+
sb.append("<UserDataId>").append(escapeText(vo.getUserDataUuid())).append("</UserDataId>");
448+
}
449+
if (vo.getAffinityGroupId() != null) {
450+
sb.append("<AffinityGroupId>").append(escapeText(vo.getAffinityGroupUuid())).append("</AffinityGroupId>");
451+
}
452+
if (vm.getNics() != null && CollectionUtils.isNotEmpty(vm.getNics().getItems())) {
453+
sb.append("<Nics>");
454+
for (Nic nic : nics(vm)) {
455+
if (nic == null || StringUtils.isBlank(nic.getId())) {
456+
continue;
457+
}
458+
String networkId = nicNetworkId(nic);
459+
if (networkId == null) {
460+
continue;
461+
}
462+
sb.append("<Nic>");
463+
sb.append("<Id>").append(escapeText(nic.getId())).append("</Id>");
464+
sb.append("<NetworkId>").append(escapeText(networkId)).append("</NetworkId>");
465+
sb.append("<MACAddress>").append(escapeText(nicMac(nic))).append("</MACAddress>");
466+
sb.append("<Ip4Address>").append(escapeText(nicIp(nic, "v4"))).append("</Ip4Address>");
467+
sb.append("<Ip6Address>").append(escapeText(nicIp(nic, "v6"))).append("</Ip6Address>");
468+
sb.append("</Nic>");
469+
}
470+
sb.append("</Nics>");
471+
}
472+
if (StringUtils.isNotBlank(vm.getSshKeyPairNames())) {
473+
sb.append("<SshKeyPairs>").append(escapeText(vm.getSshKeyPairNames())).append("</SshKeyPairs>");
474+
}
475+
if (StringUtils.isNotBlank(vm.getGuestOsId())) {
476+
sb.append("<GuestOsId>").append(escapeText(vm.getGuestOsId())).append("</GuestOsId>");
477+
}
478+
if (StringUtils.isNotBlank(vm.getGuestOsName())) {
479+
sb.append("<GuestOsName>").append(escapeText(vm.getGuestOsName())).append("</GuestOsName>");
480+
}
481+
sb.append("</CloudStack>");
482+
sb.append("</Section>");
483+
}
484+
}
485+
473486
protected static String getVmConfigurationData(Vm vm) {
474487
Vm.Initialization initialization = vm.getInitialization();
475488
if (initialization == null) {
@@ -758,6 +771,18 @@ private static void updateFromXmlCloudStackMetadataSection(Vm vm, Node metadataS
758771
if (StringUtils.isNotBlank(userDataId)) {
759772
vm.setUserDataId(userDataId);
760773
}
774+
String sshKeyPairs = xpathString(xpath, metadataSection, ".//*[local-name()='SshKeyPairs']/text()");
775+
if (StringUtils.isNotBlank(sshKeyPairs)) {
776+
vm.setSshKeyPairNames(sshKeyPairs);
777+
}
778+
String guestOsId = xpathString(xpath, metadataSection, ".//*[local-name()='GuestOsId']/text()");
779+
if (StringUtils.isNotBlank(guestOsId)) {
780+
vm.setGuestOsId(guestOsId);
781+
}
782+
String guestOsName = xpathString(xpath, metadataSection, ".//*[local-name()='GuestOsName']/text()");
783+
if (StringUtils.isNotBlank(guestOsName)) {
784+
vm.setGuestOsId(guestOsName);
785+
}
761786
final Map<String, String> details = new HashMap<>();
762787
try {
763788
NodeList detailNodes = (NodeList) xpath.evaluate(

0 commit comments

Comments
 (0)