From 239ee80a888756c988c2ea45295273a397e9e0a2 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Tue, 14 Dec 2021 18:25:07 +0530 Subject: [PATCH] - Support to patch SystemVMs - VMWare - Remove attaching systemvm.iso to systemVMs - Modify / Refactor VMware start command to copy patch related files to the systemvms - cleanup --- .../vmware/resource/VmwareResource.java | 114 +++++++++++++++++- .../CitrixPatchSystemVmCommandWrapper.java | 19 ++- scripts/vm/hypervisor/xenserver/vmops | 16 ++- .../java/com/cloud/vm/UserVmManagerImpl.java | 2 +- 4 files changed, 136 insertions(+), 15 deletions(-) diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index ed3739182f0..669ae088da1 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -48,6 +48,10 @@ import java.util.stream.Collectors; import javax.naming.ConfigurationException; import javax.xml.datatype.XMLGregorianCalendar; +import com.cloud.agent.api.PatchSystemVmAnswer; +import com.cloud.agent.api.PatchSystemVmCommand; +import com.cloud.resource.ServerResourceBase; +import com.cloud.utils.FileUtil; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; @@ -362,9 +366,10 @@ import com.vmware.vim25.VmConfigSpec; import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; -public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService, VirtualRouterDeployer { +public class VmwareResource extends ServerResourceBase implements StoragePoolResource, ServerResource, VmwareHostService, VirtualRouterDeployer { private static final Logger s_logger = Logger.getLogger(VmwareResource.class); public static final String VMDK_EXTENSION = ".vmdk"; + public static final String BASEPATH = "/usr/share/cloudstack-common/vms/"; private static final Random RANDOM = new Random(System.nanoTime()); @@ -594,7 +599,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa answer = execute((SetupPersistentNetworkCommand) cmd); } else if (clz == GetVmVncTicketCommand.class) { answer = execute((GetVmVncTicketCommand) cmd); - } else { + } else if (clz == PatchSystemVmCommand.class) { + answer = execute((PatchSystemVmCommand) cmd); + } else { answer = Answer.createUnsupportedCommandAnswer(cmd); } @@ -634,6 +641,68 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return answer; } + private ExecutionResult getSystemVmVersionAndChecksum(String controlIp) { + ExecutionResult result; + try { + result = executeInVR(controlIp, VRScripts.VERSION, null); + if (!result.isSuccess()) { + String errMsg = String.format("GetSystemVMVersionCmd on %s failed, message %s", controlIp, result.getDetails()); + s_logger.error(errMsg); + throw new CloudRuntimeException(errMsg); + } + } catch (final Exception e) { + final String msg = "GetSystemVMVersionCmd failed due to " + e; + s_logger.error(msg, e); + throw new CloudRuntimeException(msg, e); + } + return result; + } + + private Answer execute(PatchSystemVmCommand cmd) { + String controlIp = getRouterSshControlIp(cmd); + String sysVMName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String homeDir = System.getProperty("user.home"); + File pemFile = new File(homeDir + "/.ssh/id_rsa"); + + ExecutionResult result = getSystemVmVersionAndChecksum(controlIp); + try { + FileUtil.scpPatchFiles(controlIp, "/home/cloud", DefaultDomRSshPort, pemFile, newSrcFiles, BASEPATH); + } catch (CloudRuntimeException e) { + return new PatchSystemVmAnswer(cmd, e.getMessage()); + } + + final String[] lines = result.getDetails().split("&"); + // TODO: do we fail, or patch anyway?? + if (lines.length != 2) { + return new PatchSystemVmAnswer(cmd, result.getDetails()); + } + + String scriptChecksum = lines[1].trim(); + String checksum = calculateCurrentChecksum(sysVMName).trim(); + + if (!org.apache.commons.lang3.StringUtils.isEmpty(checksum) && checksum.equals(scriptChecksum)) { + if (!cmd.isForced()) { + String msg = String.format("No change in the scripts checksum, not patching systemVM %s", sysVMName); + s_logger.info(msg); + return new PatchSystemVmAnswer(cmd, msg, lines[0], lines[1]); + } + } + + Pair patchResult = null; + try { + patchResult = SshHelper.sshExecute(controlIp, DefaultDomRSshPort, "root", + pemFile, null, "/home/cloud/patch-sysvms.sh", 10000, 10000, 60000); + } catch (Exception e) { + return new PatchSystemVmAnswer(cmd, e.getMessage()); + } + + if (patchResult.first()) { + return new PatchSystemVmAnswer(cmd, String.format("Successfully patched systemVM %s ", sysVMName), lines[0], lines[1]); + } + return new PatchSystemVmAnswer(cmd, patchResult.second()); + + } + private Answer execute(SetupPersistentNetworkCommand cmd) { VmwareHypervisorHost host = getHyperHost(getServiceContext()); String hostname = null; @@ -2100,7 +2169,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, - String.format("[%s] systemvm/%s", secDsMo.getName(), mgr.getSystemVMIsoFileNameOnDatastore()), secDsMo.getMor(), true, true, ideUnitNumber++, i + 1); + null, secDsMo.getMor(), true, true, ideUnitNumber++, i + 1); deviceConfigSpecArray[i].setDevice(isoInfo.first()); if (isoInfo.second()) { if (s_logger.isDebugEnabled()) @@ -2485,6 +2554,29 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa startAnswer.setIqnToData(iqnToData); + if (vmSpec.getType() != VirtualMachine.Type.User) { + String controlIp = getControlIp(nics); + // check if the router is up? + for (int count = 0; count < 60; count++) { + final boolean result = _vrResource.connect(controlIp, 1, 5000); + if (result) { + break; + } + } + + try { + String homeDir = System.getProperty("user.home"); + File pemFile = new File(homeDir + "/.ssh/id_rsa"); + FileUtil.scpPatchFiles(controlIp, "/home/cloud", DefaultDomRSshPort, pemFile, newSrcFiles, BASEPATH); + // TODO: May want to remove this when cert patching logic is moved + Thread.sleep(10000); + } catch (Exception e) { + String errMsg = "Failed to scp files to system VM. Patching of systemVM failed"; + s_logger.error(errMsg, e); + return new StartAnswer(cmd, String.format("%s due to: %s", errMsg, e.getMessage())); + } + } + // Since VM was successfully powered-on, if there was an existing VM in a different cluster that was unregistered, delete all the files associated with it. if (existingVmName != null && existingVmFileLayout != null) { List vmDatastoreNames = new ArrayList(); @@ -3886,6 +3978,17 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } + private String getControlIp(NicTO[] nicTOs) { + String controlIpAddress = null; + for (NicTO nic : nicTOs) { + if ((TrafficType.Management == nic.getType() || TrafficType.Control == nic.getType()) && nic.getIp() != null) { + controlIpAddress = nic.getIp(); + break; + } + } + return controlIpAddress; + } + private VirtualMachineMO takeVmFromOtherHyperHost(VmwareHypervisorHost hyperHost, String vmName) throws Exception { VirtualMachineMO vmMo = hyperHost.findVmOnPeerHyperHost(vmName); @@ -6973,6 +7076,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return _name; } + @Override + protected String getDefaultScriptsDir() { + return null; + } + @Override public boolean start() { return true; diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixPatchSystemVmCommandWrapper.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixPatchSystemVmCommandWrapper.java index e27381e694d..a6b183f9d11 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixPatchSystemVmCommandWrapper.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixPatchSystemVmCommandWrapper.java @@ -25,10 +25,8 @@ import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.utils.ExecutionResult; -import com.cloud.utils.FileUtil; -import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.ssh.SshHelper; +import com.xensource.xenapi.Connection; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; @@ -44,12 +42,13 @@ public class CitrixPatchSystemVmCommandWrapper extends CommandWrapper patchResult = null; + String patchResult = null; try { - patchResult = SshHelper.sshExecute(controlIp, sshPort, "root", - pemFile, null, "/home/cloud/patch-sysvms.sh", 10000, 10000, 60000); + patchResult = serverResource.callHostPlugin(conn, "vmops", "runPatchScriptInDomr", "domrip", controlIp); } catch (Exception e) { return new PatchSystemVmAnswer(command, e.getMessage()); } - if (patchResult.first()) { + + if (patchResult.startsWith("succ#")) { return new PatchSystemVmAnswer(command, String.format("Successfully patched systemVM %s ", sysVMName), lines[0], lines[1]); } - return new PatchSystemVmAnswer(command, patchResult.second()); + return new PatchSystemVmAnswer(command, patchResult.substring(5)); } diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops index e5c7e1a3788..cea47cce297 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -248,6 +248,19 @@ def createFileInDomr(session, args): txt = 'fail#' + txt return txt +@echo +def runPatchScriptInDomr(session, args): + domrip = args['domrip'] + txt="" + try: + target = "root@" + domrip + txt = util.pread2(['ssh','-p','3922','-i','/root/.ssh/id_rsa.cloud', target, "/bin/bash","/home/cloud/patch-sysvms.sh"]) + txt = 'succ#' + txt + except: + logging.debug("failed to run patch script in systemVM with IP: " + domrip) + txt = 'fail#' + txt + return txt + @echo def deleteFile(session, args): file_path = args["filepath"] @@ -1590,4 +1603,5 @@ if __name__ == "__main__": "cleanup_rules":cleanup_rules, "createFileInDomr":createFileInDomr, "kill_copy_process":kill_copy_process, - "secureCopyToHost":secureCopyToHost}) + "secureCopyToHost":secureCopyToHost, + "runPatchScriptInDomr": runPatchScriptInDomr}) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index d7c90b305c8..a3b7c47d592 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -4874,7 +4874,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } @Override - public boolean finalizeStart(VirtualMachineProfile profile, long hostId, Commands cmds, ReservationContext context) { + public boolean finalizeStart(VirtualMachineProfile profile, long hostId, Commands cmds, ReservationContext context) { UserVmVO vm = _vmDao.findById(profile.getId()); Answer[] answersToCmds = cmds.getAnswers();