diff --git a/client/pom.xml b/client/pom.xml index 0607642c0cf4..0d8547d5ebef 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -511,6 +511,12 @@ + + + + + + diff --git a/core/src/com/cloud/agent/api/CopyFileInVmAnswer.java b/core/src/com/cloud/agent/api/CopyFileInVmAnswer.java new file mode 100644 index 000000000000..bc0939be576c --- /dev/null +++ b/core/src/com/cloud/agent/api/CopyFileInVmAnswer.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.agent.api; + +public class CopyFileInVmAnswer extends Answer { + public CopyFileInVmAnswer() { + } + + public CopyFileInVmAnswer(CopyFileInVmCommand cmd) { + super(cmd, true, null); + } + + public CopyFileInVmAnswer(CopyFileInVmCommand cmd, String details) { + super(cmd, false, details); + } + + public CopyFileInVmAnswer(CopyFileInVmCommand cmd, Exception e) { + super(cmd, false, e.getMessage()); + } +} diff --git a/core/src/com/cloud/agent/api/CopyFileInVmCommand.java b/core/src/com/cloud/agent/api/CopyFileInVmCommand.java new file mode 100644 index 000000000000..99e9552ffe68 --- /dev/null +++ b/core/src/com/cloud/agent/api/CopyFileInVmCommand.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.agent.api; + + +public class CopyFileInVmCommand extends Command { + String src; + String dest; + String vmIp; + + public CopyFileInVmCommand(String src, String dest, String vmIp) { + super(); + this.src = src; + this.dest = dest; + this.vmIp = vmIp; + } + + public String getSrc() { + return src; + } + + public String getDest() { + return dest; + } + + public String getVmIp() { + return vmIp; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java index 862cc30a2e5b..974c8f057760 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java @@ -32,6 +32,9 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; import org.joda.time.Duration; import java.util.ArrayList; import java.util.Collections; @@ -70,6 +73,8 @@ import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; import com.cloud.agent.api.CheckS2SVpnConnectionsCommand; import com.cloud.agent.api.Command; +import com.cloud.agent.api.CopyFileInVmAnswer; +import com.cloud.agent.api.CopyFileInVmCommand; import com.cloud.agent.api.GetDomRVersionAnswer; import com.cloud.agent.api.GetDomRVersionCmd; import com.cloud.agent.api.GetVmConfigAnswer; @@ -491,6 +496,8 @@ public final Answer executeRequest(final Command cmd) { answer = execute((UnPlugNicCommand)cmd); } else if (clazz == CopyCommand.class) { answer = execute((CopyCommand)cmd); + } else if (clazz == CopyFileInVmCommand.class) { + answer = execute((CopyFileInVmCommand)cmd); } else { if (clazz == StartCommand.class) { @@ -595,6 +602,25 @@ private PlugNicAnswer execute(final PlugNicCommand cmd) { } } + private CopyFileInVmAnswer execute(CopyFileInVmCommand cmd) { + File keyFile = getSystemVMKeyFile(); + try { + File file = new File(cmd.getSrc()); + if(file.exists()) { + if(file.isDirectory()) { + for (File f : FileUtils.listFiles(new File(cmd.getSrc()), TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)) { + SshHelper.scpTo(cmd.getVmIp(), 3922, "root", keyFile, null, cmd.getDest(), f.getCanonicalPath(), null); + } + } else { + SshHelper.scpTo(cmd.getVmIp(), 3922, "root", keyFile, null, cmd.getDest(), file.getCanonicalPath(), null); + } + } + } catch (Exception e) { + s_logger.error("Fail to copy file " + cmd.getSrc() + " in VM " + cmd.getVmIp(), e); + return new CopyFileInVmAnswer(cmd, e); + } + return new CopyFileInVmAnswer(cmd); + } private UnPlugNicAnswer execute(final UnPlugNicCommand cmd) { if (s_logger.isInfoEnabled()) { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyFileInVmCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyFileInVmCommandWrapper.java new file mode 100644 index 000000000000..8c699f0204a4 --- /dev/null +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyFileInVmCommandWrapper.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.hypervisor.kvm.resource.wrapper; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CopyFileInVmAnswer; +import com.cloud.agent.api.CopyFileInVmCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.utils.ssh.SshHelper; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; +import org.apache.log4j.Logger; + +import java.io.File; + +@ResourceWrapper(handles = CopyFileInVmCommand.class) +public class LibvirtCopyFileInVmCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(LibvirtCopyFileInVmCommandWrapper.class); + + @Override public Answer execute(CopyFileInVmCommand cmd, LibvirtComputingResource libvirtComputingResource) { + final File keyFile = new File("/root/.ssh/id_rsa.cloud"); + try { + File file = new File(cmd.getSrc()); + if(file.exists()) { + if(file.isDirectory()) { + for (File f : FileUtils.listFiles(new File(cmd.getSrc()), TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)) { + SshHelper.scpTo(cmd.getVmIp(), 3922, "root", keyFile, null, cmd.getDest(), f.getCanonicalPath(), null); + } + } else { + SshHelper.scpTo(cmd.getVmIp(), 3922, "root", keyFile, null, cmd.getDest(), file.getCanonicalPath(), null); + } + } + } catch (Exception e) { + s_logger.error("Fail to copy file " + cmd.getSrc() + " in VM " + cmd.getVmIp(), e); + return new CopyFileInVmAnswer(cmd, e); + } + return new CopyFileInVmAnswer(cmd); + } +} diff --git a/plugins/hypervisors/ovm3/src/main/java/com/cloud/hypervisor/ovm3/resources/Ovm3HypervisorResource.java b/plugins/hypervisors/ovm3/src/main/java/com/cloud/hypervisor/ovm3/resources/Ovm3HypervisorResource.java index 5af2b8f0a210..755db2e82335 100644 --- a/plugins/hypervisors/ovm3/src/main/java/com/cloud/hypervisor/ovm3/resources/Ovm3HypervisorResource.java +++ b/plugins/hypervisors/ovm3/src/main/java/com/cloud/hypervisor/ovm3/resources/Ovm3HypervisorResource.java @@ -26,6 +26,8 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.agent.api.CopyFileInVmAnswer; +import com.cloud.agent.api.CopyFileInVmCommand; import org.apache.log4j.Logger; import org.apache.cloudstack.storage.command.AttachCommand; @@ -284,9 +286,12 @@ public Answer executeRequest(Command cmd) { return execute((StopCommand) cmd); } else if (clazz == RebootCommand.class) { return execute((RebootCommand) cmd); + } else if (clazz == CopyFileInVmCommand.class) { + return execute((CopyFileInVmCommand)cmd); + } else { + LOGGER.debug("Can't find class for executeRequest " + cmd.getClass() + ", is your direct call missing?"); + return Answer.createUnsupportedCommandAnswer(cmd); } - LOGGER.debug("Can't find class for executeRequest " + cmd.getClass() +", is your direct call missing?"); - return Answer.createUnsupportedCommandAnswer(cmd); } @Override @@ -589,6 +594,12 @@ public RebootAnswer execute(RebootCommand cmd) { } } + private CopyFileInVmAnswer execute(CopyFileInVmCommand cmd) { + //this method needs to be implemented later to actually copy files + //this is here so that console proxy vm deployment doesn't fail + return new CopyFileInVmAnswer(cmd); + } + @Override protected String getDefaultScriptsDir() { return null; diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManager.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManager.java index df2703d0b085..8e82a7334a25 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManager.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManager.java @@ -24,6 +24,8 @@ import com.cloud.agent.api.CheckRouterCommand; import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.CleanupNetworkRulesCmd; +import com.cloud.agent.api.CopyFileInVmAnswer; +import com.cloud.agent.api.CopyFileInVmCommand; import com.cloud.agent.api.CreateVMSnapshotCommand; import com.cloud.agent.api.DeleteVMSnapshotCommand; import com.cloud.agent.api.FenceCommand; @@ -79,6 +81,8 @@ public interface MockVmManager extends Manager { CheckSshAnswer checkSshCommand(CheckSshCommand cmd); + CopyFileInVmAnswer copyFileInVmCommand(CopyFileInVmCommand cmd); + Answer setVmData(VmDataCommand cmd); Answer checkConsoleProxyLoad(CheckConsoleProxyLoadCommand cmd); diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java index 6eaf09cad856..21091ebb609b 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java @@ -25,6 +25,8 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.agent.api.CopyFileInVmAnswer; +import com.cloud.agent.api.CopyFileInVmCommand; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -367,6 +369,9 @@ public CheckSshAnswer checkSshCommand(final CheckSshCommand cmd) { return new CheckSshAnswer(cmd); } + @Override + public CopyFileInVmAnswer copyFileInVmCommand(CopyFileInVmCommand cmd) { return new CopyFileInVmAnswer(cmd);} + @Override public MigrateAnswer migrate(final MigrateCommand cmd, final SimulatorInfo info) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.SIMULATOR_DB); diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java index 03593392ba7e..399489ffb17c 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java @@ -26,6 +26,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.agent.api.CopyFileInVmCommand; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.DownloadCommand; import org.apache.cloudstack.storage.command.DownloadProgressCommand; @@ -288,6 +289,8 @@ public Answer simulate(final Command cmd, final String hostGuid) { answer = _mockVmMgr.startVM((StartCommand)cmd, info); } else if (cmd instanceof CheckSshCommand) { answer = _mockVmMgr.checkSshCommand((CheckSshCommand)cmd); + } else if (cmd instanceof CopyFileInVmCommand) { + answer = _mockVmMgr.copyFileInVmCommand((CopyFileInVmCommand)cmd); } else if (cmd instanceof CheckVirtualMachineCommand) { answer = _mockVmMgr.checkVmState((CheckVirtualMachineCommand)cmd); } else if (cmd instanceof SetStaticNatRulesCommand) { diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index d4e432d54bf3..47ea3b1e0f4f 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -43,6 +43,10 @@ import javax.naming.ConfigurationException; +import com.cloud.agent.api.CopyFileInVmAnswer; +import com.cloud.agent.api.CopyFileInVmCommand; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; import org.apache.log4j.Logger; import org.apache.log4j.NDC; @@ -492,6 +496,8 @@ public Answer executeRequest(Command cmd) { return execute((PvlanSetupCommand)cmd); } else if (clz == UnregisterNicCommand.class) { answer = execute((UnregisterNicCommand)cmd); + } else if (clz == CopyFileInVmCommand.class ) { + return execute((CopyFileInVmCommand)cmd); } else { answer = Answer.createUnsupportedCommandAnswer(cmd); } @@ -1393,6 +1399,27 @@ private static DiskTO getIsoDiskTO(DiskTO[] disks) { return null; } + private CopyFileInVmAnswer execute(CopyFileInVmCommand cmd) { + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + File keyFile = mgr.getSystemVMKeyFile(); + try { + File file = new File(cmd.getSrc()); + if(file.exists()) { + if(file.isDirectory()) { + for (File f : FileUtils.listFiles(new File(cmd.getSrc()), TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)) { + SshHelper.scpTo(cmd.getVmIp(), 3922, "root", keyFile, null, cmd.getDest(), f.getCanonicalPath(), null); + } + } else { + SshHelper.scpTo(cmd.getVmIp(), 3922, "root", keyFile, null, cmd.getDest(), file.getCanonicalPath(), null); + } + } + } catch (Exception e) { + s_logger.error("Fail to copy file " + cmd.getSrc() + " in VM " + cmd.getVmIp(), e); + return new CopyFileInVmAnswer(cmd, e); + } + return new CopyFileInVmAnswer(cmd); + } + protected ScaleVmAnswer execute(ScaleVmCommand cmd) { VmwareContext context = getServiceContext(); diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java index 1ebe51546b58..be98b0bbce17 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java @@ -4526,7 +4526,7 @@ public void scaleVM(final Connection conn, final VM vm, final VirtualMachineTO v } vm.setMemoryDynamicRange(conn, newDynamicMemoryMin, newDynamicMemoryMax); - vm.setVCPUsNumberLive(conn, (long) vmSpec.getCpus()); + vm.setVCPUsNumberLive(conn, (long)vmSpec.getCpus()); final Integer speed = vmSpec.getMinSpeed(); if (speed != null) { @@ -5114,8 +5114,8 @@ public String upgradeSnapshot(final Connection conn, final String templatePath, public void waitForTask(final Connection c, final Task task, final long pollInterval, final long timeout) throws XenAPIException, XmlRpcException, TimeoutException { final long beginTime = System.currentTimeMillis(); if (s_logger.isTraceEnabled()) { - s_logger.trace("Task " + task.getNameLabel(c) + " (" + task.getUuid(c) + ") sent to " + c.getSessionReference() + " is pending completion with a " + timeout - + "ms timeout"); + s_logger.trace( + "Task " + task.getNameLabel(c) + " (" + task.getUuid(c) + ") sent to " + c.getSessionReference() + " is pending completion with a " + timeout + "ms timeout"); } while (task.getStatus(c) == Types.TaskStatusType.PENDING) { try { @@ -5533,4 +5533,20 @@ public boolean attachConfigDriveToMigratedVm(Connection conn, String vmName, Str } + public ExecutionResult copyFileInVm(String vmIp, File file, String dest) { + final Connection conn = getConnection(); + final String hostPath = "/tmp/"; + + s_logger.debug("Copying file to VM with ip " + vmIp + " using host " + _host.getIp()); + try { + SshHelper.scpTo(_host.getIp(), 22, _username, null, _password.peek(), hostPath, file.getCanonicalPath(), null); + } catch (final Exception e) { + s_logger.warn("Fail to copy file into host " + _host.getIp() + " failed with exception " + e.getMessage().toString()); + } + + final String rc = callHostPlugin(conn, "vmops", "createFileInDomr", "domrip", vmIp, "srcfilepath", hostPath + file.getName(), "dstfilepath", dest); + s_logger.debug("File " + file.getName() + " got copied in VM, ip " + vmIp); + + return new ExecutionResult(rc.startsWith("succ#"), rc.substring(5)); + } } diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixCopyFileInVmCommandWrapper.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixCopyFileInVmCommandWrapper.java new file mode 100644 index 000000000000..77e9084b62c7 --- /dev/null +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixCopyFileInVmCommandWrapper.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.hypervisor.xenserver.resource.wrapper.xenbase; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CopyFileInVmAnswer; +import com.cloud.agent.api.CopyFileInVmCommand; +import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.utils.ExecutionResult; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; +import org.apache.log4j.Logger; + +import java.io.File; + +@ResourceWrapper(handles = CopyFileInVmCommand.class) +public class CitrixCopyFileInVmCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(CitrixCopyFileInVmCommandWrapper.class); + + @Override + public Answer execute(final CopyFileInVmCommand cmd, final CitrixResourceBase citrixResourceBase) { + try { + File file = new File(cmd.getSrc()); + if(file.exists()) { + if(file.isDirectory()) { + for(File f : FileUtils.listFiles(file, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)) { + ExecutionResult result = citrixResourceBase.copyFileInVm(cmd.getVmIp(), f, cmd.getDest()); + if(!result.isSuccess()) { + return new CopyFileInVmAnswer(cmd, result.getDetails()); + } + } + } else { + ExecutionResult result = citrixResourceBase.copyFileInVm(cmd.getVmIp(), file, cmd.getDest()); + if(!result.isSuccess()) { + return new CopyFileInVmAnswer(cmd, result.getDetails()); + } + } + } + + } catch (Exception e) { + s_logger.error("Unable to retrieve files " + cmd.getSrc() + " to copy to VM " + cmd.getVmIp()); + return new CopyFileInVmAnswer(cmd, e); + } + + return new CopyFileInVmAnswer(cmd); + } +} diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops index 0dfa6da038ed..60a79e064af2 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -214,7 +214,7 @@ def createFileInDomr(session, args): util.pread2(['rm',src_filepath]) txt = 'succ#' + txt except: - logging.debug("failed to copy file " + src_filepath + " from host to VR with ip " + domrip) + logging.debug("failed to copy file " + src_filepath + " from host to domr with ip " + domrip) txt = 'fail#' + txt return txt diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 1817ade28744..ea54c6f37baa 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.consoleproxy; +import java.io.File; +import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; @@ -29,6 +31,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.agent.api.CopyFileInVmCommand; import org.apache.cloudstack.config.ApiServiceConfiguration; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -50,7 +53,6 @@ import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupProxyCommand; -import com.cloud.agent.api.check.CheckSshAnswer; import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer; import com.cloud.agent.manager.Commands; @@ -1475,19 +1477,39 @@ public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile prof CheckSshCommand check = new CheckSshCommand(profile.getInstanceName(), controlNic.getIPv4Address(), 3922); cmds.addCommand("checkSsh", check); + try { + File uiFiles = new File("systemvm/js"); + if (!uiFiles.exists()) { + uiFiles = new File("/usr/share/cloudstack-common/systemvm/js"); + } + if (uiFiles.exists() && uiFiles.isDirectory()) { + CopyFileInVmCommand copyFile = new CopyFileInVmCommand(uiFiles.getCanonicalPath(), "/usr/local/cloud/systemvm/js", controlNic.getIPv4Address()); + cmds.addCommand("copyFile", copyFile); + } else { + s_logger.error("Couldn't locate localization files for console proxy"); + return false; + } + } catch (IOException e) { + s_logger.error("Failed to copy localization files for console proxy: " + e.getMessage()); + } + return true; } @Override public boolean finalizeStart(VirtualMachineProfile profile, long hostId, Commands cmds, ReservationContext context) { - CheckSshAnswer answer = (CheckSshAnswer)cmds.getAnswer("checkSsh"); - if (answer == null || !answer.getResult()) { - if (answer != null) { - s_logger.warn("Unable to ssh to the VM: " + answer.getDetails()); - } else { - s_logger.warn("Unable to ssh to the VM: null answer"); + for(Answer answer : cmds.getAnswers()) { + if(answer == null || !answer.getResult()) { + if (answer != null) { + s_logger.warn("Unable to ssh to the VM: " + answer.getDetails()); + } else { + s_logger.warn("Unable to ssh to the VM: null answer"); + } + if(cmds.getAnswer("copyFile") == answer) { + continue; + } + return false; } - return false; } try { diff --git a/server/src/com/cloud/server/ManagementServer.java b/server/src/com/cloud/server/ManagementServer.java index 4e58a4f55765..6d38f914e9b8 100644 --- a/server/src/com/cloud/server/ManagementServer.java +++ b/server/src/com/cloud/server/ManagementServer.java @@ -18,6 +18,7 @@ import com.cloud.host.DetailVO; import com.cloud.host.HostVO; +import com.cloud.storage.GuestOSCategoryVO; import com.cloud.storage.GuestOSHypervisorVO; import com.cloud.storage.GuestOSVO; import com.cloud.utils.Pair; @@ -55,6 +56,8 @@ public interface ManagementServer extends ManagementService, PluggableService { GuestOSVO getGuestOs(Long guestOsId); + public GuestOSCategoryVO getGuestOsCategory(final Long guestOsCategoryId); + GuestOSHypervisorVO getGuestOsHypervisor(Long guestOsHypervisorId); /** diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 5ab7c362f0d9..c4c80c29d976 100644 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -3465,6 +3465,11 @@ public GuestOSVO getGuestOs(final Long guestOsId) { return _guestOSDao.findById(guestOsId); } + @Override + public GuestOSCategoryVO getGuestOsCategory(final Long guestOsCategoryId) { + return _guestOSCategoryDao.findById(guestOsCategoryId); + } + @Override public GuestOSHypervisorVO getGuestOsHypervisor(final Long guestOsHypervisorId) { return _guestOSHypervisorDao.findById(guestOsHypervisorId); diff --git a/server/src/com/cloud/servlet/ConsoleProxyClientParam.java b/server/src/com/cloud/servlet/ConsoleProxyClientParam.java index ce06a9624a95..4f77633c62f5 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyClientParam.java +++ b/server/src/com/cloud/servlet/ConsoleProxyClientParam.java @@ -33,6 +33,13 @@ public class ConsoleProxyClientParam { private String username; private String password; + //Below parameters are needed to help implement keyboard + private String guestos; + //display name contains version specific details which may be needed for keyboard mappings + private String guestosDisplayName; + private String hypervisor; + private String hypervisorVersion; + public ConsoleProxyClientParam() { clientHostPort = 0; } @@ -140,4 +147,36 @@ public void setPassword(String password) { public String getPassword() { return password; } + + public String getGuestos() { + return guestos; + } + + public void setGuestos(String guestos) { + this.guestos = guestos; + } + + public String getGuestosDisplayName() { + return guestosDisplayName; + } + + public void setGuestosDisplayName(String guestosDisplayName) { + this.guestosDisplayName = guestosDisplayName; + } + + public String getHypervisor() { + return hypervisor; + } + + public void setHypervisor(String hypervisor) { + this.hypervisor = hypervisor; + } + + public String getHypervisorVersion() { + return hypervisorVersion; + } + + public void setHypervisorVersion(String hypervisorVersion) { + this.hypervisorVersion = hypervisorVersion; + } } diff --git a/server/src/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index cc788c7b1183..ba80bc5d9dfe 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -35,6 +35,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import com.cloud.storage.GuestOSCategoryVO; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -461,14 +462,17 @@ private String composeConsoleAccessUrl(String rootUrl, VirtualMachine vm, HostVO param.setClientTunnelSession(parsedHostInfo.third()); } - sb.append("/ajax?token=" + encryptor.encryptObject(ConsoleProxyClientParam.class, param)); - - // for console access, we need guest OS type to help implement keyboard + // for console access, we need below parameters to help implement keyboard long guestOs = vm.getGuestOSId(); GuestOSVO guestOsVo = _ms.getGuestOs(guestOs); - if (guestOsVo.getCategoryId() == 6) - sb.append("&guest=windows"); + GuestOSCategoryVO guestOSCategoryVO = _ms.getGuestOsCategory(guestOsVo.getCategoryId()); + param.setGuestos(guestOSCategoryVO.getName()); + param.setGuestosDisplayName(guestOsVo.getDisplayName()); + param.setHypervisor(hostVo.getHypervisorType().name()); + param.setHypervisorVersion(hostVo.getHypervisorVersion()); + + sb.append("/ajax?token=" + encryptor.encryptObject(ConsoleProxyClientParam.class, param)); if (s_logger.isDebugEnabled()) { s_logger.debug("Compose console url: " + sb.toString()); } diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java index f586c9361f10..9ba3305d8613 100644 --- a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java @@ -85,6 +85,10 @@ private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException String hypervHost = queryMap.get("hypervHost"); String username = queryMap.get("username"); String password = queryMap.get("password"); + String guestos = queryMap.get("guestos"); + String guestosDisplayName = queryMap.get("guestosDisplayName"); + String hypervisor = queryMap.get("hypervisor"); + String hypervisorVersion = queryMap.get("hypervisorVersion"); if (tag == null) tag = ""; @@ -136,6 +140,10 @@ private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException param.setHypervHost(hypervHost); param.setUsername(username); param.setPassword(password); + param.setGuestos(guestos); + param.setGuestosDisplayName(guestosDisplayName); + param.setHypervisorVersion(hypervisorVersion); + param.setHypervisor(hypervisor); viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr); } catch (Exception e) { @@ -179,8 +187,7 @@ private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException s_logger.debug("Ajax request indicates a fresh client start"); String title = queryMap.get("t"); - String guest = queryMap.get("guest"); - handleClientStart(t, viewer, title != null ? title : "", guest); + handleClientStart(t, viewer, title != null ? title : ""); } else { if (s_logger.isTraceEnabled()) @@ -377,9 +384,9 @@ private void handleClientKickoff(HttpExchange t, ConsoleProxyClient viewer) thro } } - private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title, String guest) throws IOException { + private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title) throws IOException { List languages = t.getRequestHeaders().get("Accept-Language"); - String response = viewer.onAjaxClientStart(title, languages, guest); + String response = viewer.onAjaxClientStart(title, languages); Headers hds = t.getResponseHeaders(); hds.set("Content-Type", "text/html"); diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java index 6429de4ad2fa..1dcd49ee920e 100644 --- a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java @@ -44,7 +44,7 @@ public interface ConsoleProxyClient { Image getClientScaledImage(int width, int height); // client thumbnail support - String onAjaxClientStart(String title, List languages, String guest); + String onAjaxClientStart(String title, List languages); String onAjaxClientUpdate(); diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java index 100e00c5284d..3084937fec41 100644 --- a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java @@ -242,7 +242,7 @@ private String onAjaxClientConnectFailed() { } @Override - public String onAjaxClientStart(String title, List languages, String guest) { + public String onAjaxClientStart(String title, List languages) { updateFrontEndActivityTime(); if (!waitForViewerReady()) @@ -275,13 +275,12 @@ public String onAjaxClientStart(String title, List languages, String gue i++; } - return getAjaxViewerPageContent(sbTileSequence.toString(), imgUrl, updateUrl, width, height, tileWidth, tileHeight, title, - ConsoleProxy.keyboardType == ConsoleProxy.KEYBOARD_RAW, languages, guest, this.clientParam.getLocale()); + return getAjaxViewerPageContent(sbTileSequence.toString(), imgUrl, updateUrl, width, height, tileWidth, tileHeight, title, languages, clientParam.getLocale(), + clientParam.getGuestos(), clientParam.getGuestosDisplayName(), clientParam.getHypervisor(), clientParam.getHypervisorVersion()); } - private String getAjaxViewerPageContent(String tileSequence, String imgUrl, String updateUrl, int width, int height, int tileWidth, int tileHeight, String title, - boolean rawKeyboard, List languages, String guest, String locale) { - + private String getAjaxViewerPageContent(String tileSequence, String imgUrl, String updateUrl, int width, int height, int tileWidth, int tileHeight, + String title, List languages, String locale, String guestos, String guestosDisplayName, String hypervisor, String hypervisorVersion) { StringBuffer sbLanguages = new StringBuffer(""); if (languages != null) { for (String lang : languages) { @@ -292,6 +291,9 @@ private String getAjaxViewerPageContent(String tileSequence, String imgUrl, Stri } } + if(locale == null || locale.isEmpty()) { + locale = "us"; + } String[] content = new String[] {"", "", "", "", @@ -305,16 +307,15 @@ private String getAjaxViewerPageContent(String tileSequence, String imgUrl, Stri "", "\"Ctrl-Esc\"Ctrl-Esc", "", "", - "
  • ", "", - "\"Keyboard\"Keyboard", "", "", "
  • ", "", + "
  • ", + "\"Keyboard\"", + "", + "
  • ", "", "", "
    ", " + diff --git a/ui/l10n/en.js b/ui/l10n/en.js index f25e0b70486f..1d4cd62a3610 100644 --- a/ui/l10n/en.js +++ b/ui/l10n/en.js @@ -1791,6 +1791,7 @@ var dictionary = {"ICMP.code":"ICMP Code", "label.zoneWizard.trafficType.public":"Public: Traffic between the internet and virtual machines in the cloud.", "label.zoneWizard.trafficType.storage":"Storage: Traffic between primary and secondary storage servers, such as VM templates and snapshots", "label.zones":"Zones", +"label.french.azerty.keyboard":"French AZERTY keyboard", "managed.state":"Managed State", "message.XSTools61plus.update.failed":"Failed to update Original XS Version is 6.1+ field. Error:", "message.Zone.creation.complete":"Zone creation complete", diff --git a/ui/scripts/consoleKeyboardOptions.js b/ui/scripts/consoleKeyboardOptions.js new file mode 100644 index 000000000000..8e7e052ee16a --- /dev/null +++ b/ui/scripts/consoleKeyboardOptions.js @@ -0,0 +1,21 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +var keyboardOptions = {"us": "label.standard.us.keyboard", + "uk": "label.uk.keyboard", + "fr": "label.french.azerty.keyboard", + "jp": "label.japanese.keyboard", + "sc": "label.simplified.chinese.keyboard"} \ No newline at end of file