diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java new file mode 100644 index 00000000000..1545214781c --- /dev/null +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java @@ -0,0 +1,82 @@ +// +// 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.GetVmIpAddressCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.script.Script; +import org.apache.log4j.Logger; + +@ResourceWrapper(handles = GetVmIpAddressCommand.class) +public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(LibvirtGetVmIpAddressCommandWrapper.class); + + @Override + public Answer execute(final GetVmIpAddressCommand command, final LibvirtComputingResource libvirtComputingResource) { + String ip = null; + boolean result = false; + String networkCidr = command.getVmNetworkCidr(); + if(!command.isWindows()) { + //List all dhcp lease files inside guestVm + String leasesList = Script.runSimpleBashScript(new StringBuilder().append("virt-ls ").append(command.getVmName()) + .append(" /var/lib/dhclient/ | grep .*\\*.leases").toString()); + if(leasesList != null) { + String[] leasesFiles = leasesList.split("\n"); + for(String leaseFile : leasesFiles){ + //Read from each dhclient lease file inside guest Vm using virt-cat libguestfs ulitiy + String ipAddr = Script.runSimpleBashScript(new StringBuilder().append("virt-cat ").append(command.getVmName()) + .append(" /var/lib/dhclient/" + leaseFile + " | tail -16 | grep 'fixed-address' | awk '{print $2}' | sed -e 's/;//'").toString()); + // Check if the IP belongs to the network + if((ipAddr != null) && NetUtils.isIpWithtInCidrRange(ipAddr, networkCidr)){ + ip = ipAddr; + break; + } + s_logger.debug("GetVmIp: "+command.getVmName()+ " Ip: "+ipAddr+" does not belong to network "+networkCidr); + } + } + } else { + // For windows, read from guest Vm registry using virt-win-reg libguestfs ulitiy. Registry Path: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Tcpip\Parameters\Interfaces\\DhcpIPAddress + String ipList = Script.runSimpleBashScript(new StringBuilder().append("virt-win-reg --unsafe-printable-strings ").append(command.getVmName()) + .append(" 'HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces' | grep DhcpIPAddress | awk -F : '{print $2}' | sed -e 's/^\"//' -e 's/\"$//'").toString()); + if(ipList != null) { + s_logger.debug("GetVmIp: "+command.getVmName()+ "Ips: "+ipList); + String[] ips = ipList.split("\n"); + for (String ipAddr : ips){ + // Check if the IP belongs to the network + if((ipAddr != null) && NetUtils.isIpWithtInCidrRange(ipAddr, networkCidr)){ + ip = ipAddr; + break; + } + s_logger.debug("GetVmIp: "+command.getVmName()+ " Ip: "+ipAddr+" does not belong to network "+networkCidr); + } + } + } + if(ip != null){ + result = true; + s_logger.debug("GetVmIp: "+command.getVmName()+ " Found Ip: "+ip); + } + return new Answer(command, result, ip); + } +} \ No newline at end of file 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 d4e432d54bf..d2f54954c29 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 @@ -290,6 +290,9 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VirtualMachineName; import com.cloud.vm.VmDetailConstants; +import com.vmware.vim25.GuestInfo; +import com.vmware.vim25.VirtualMachineToolsStatus; +import com.cloud.agent.api.GetVmIpAddressCommand; public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService, VirtualRouterDeployer { private static final Logger s_logger = Logger.getLogger(VmwareResource.class); @@ -490,6 +493,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return execute((ScaleVmCommand)cmd); } else if (clz == PvlanSetupCommand.class) { return execute((PvlanSetupCommand)cmd); + } else if (clz == GetVmIpAddressCommand.class) { + return execute((GetVmIpAddressCommand)cmd); } else if (clz == UnregisterNicCommand.class) { answer = execute((UnregisterNicCommand)cmd); } else { @@ -3221,11 +3226,17 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_logger.info("Executing resource RebootCommand: " + _gson.toJson(cmd)); } + boolean toolsInstallerMounted = false; + VirtualMachineMO vmMo = null; VmwareContext context = getServiceContext(); VmwareHypervisorHost hyperHost = getHyperHost(context); try { - VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(cmd.getVmName()); + vmMo = hyperHost.findVmOnHyperHost(cmd.getVmName()); if (vmMo != null) { + if (vmMo.isToolsInstallerMounted()) { + toolsInstallerMounted = true; + s_logger.trace("Detected mounted vmware tools installer for :[" + cmd.getVmName() + "]"); + } try { vmMo.rebootGuest(); return new RebootAnswer(cmd, "reboot succeeded", true); @@ -3257,6 +3268,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa String msg = "RebootCommand failed due to " + VmwareHelper.getExceptionMessage(e); s_logger.error(msg); return new RebootAnswer(cmd, msg, false); + } finally { + if (toolsInstallerMounted) { + try { + vmMo.mountToolsInstaller(); + s_logger.debug("Successfully re-mounted vmware tools installer for :[" + cmd.getVmName() + "]"); + } catch (Exception e) { + s_logger.warn("Unabled to re-mount vmware tools installer for :[" + cmd.getVmName() + "]"); + } + } } } @@ -4366,6 +4386,59 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } + protected Answer execute(GetVmIpAddressCommand cmd) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Executing resource command GetVmIpAddressCommand: " + _gson.toJson(cmd)); + } + + String details = "Unable to find IP Address of VM. "; + String vmName = cmd.getVmName(); + boolean result = false; + String ip = null; + Answer answer = null; + + VmwareContext context = getServiceContext(); + VmwareHypervisorHost hyperHost = getHyperHost(context); + + if (vmName == null || vmName.isEmpty()) { + details += "Name of instance provided is NULL or empty."; + return new Answer(cmd, result, details); + } + + try { + VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmName); + if (vmMo != null) { + GuestInfo guestInfo = vmMo.getGuestInfo(); + VirtualMachineToolsStatus toolsStatus = guestInfo.getToolsStatus(); + if (toolsStatus == VirtualMachineToolsStatus.TOOLS_NOT_INSTALLED) { + details += "Vmware tools not installed."; + } else { + ip = guestInfo.getIpAddress(); + if (ip != null) { + result = true; + } + details = ip; + } + } else { + details += "VM " + vmName + " no longer exists on vSphere host: " + hyperHost.getHyperHostName(); + s_logger.info(details); + } + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + invalidateServiceContext(); + } + details += "Encountered exception : " + VmwareHelper.getExceptionMessage(e); + s_logger.error(details); + } + + answer = new Answer(cmd, result, details); + if (s_logger.isTraceEnabled()) { + s_logger.trace("Returning GetVmIpAddressAnswer: " + _gson.toJson(answer)); + } + return answer; + } + @Override public PrimaryStorageDownloadAnswer execute(PrimaryStorageDownloadCommand cmd) { if (s_logger.isInfoEnabled()) { diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 176c7e942e1..d00b52d62c9 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -477,6 +477,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir protected IpAddressManager _ipAddrMgr; protected ScheduledExecutorService _executor = null; + protected ScheduledExecutorService _vmIpFetchExecutor = null; protected int _expungeInterval; protected int _expungeDelay; protected boolean _dailyOrHourly = false; @@ -514,6 +515,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir static final ConfigKey VmIpFetchThreadPoolMax = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmipFetch.threadPool.max", "10", "number of threads for fetching vms ip address", true); + static final ConfigKey VmIpFetchTaskWorkers = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmipfetchtask.workers", "10", + "number of worker threads for vm ip fetch task ", true); + @Override public UserVmVO getVirtualMachine(long vmId) { @@ -1947,6 +1951,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("UserVm-Scavenger")); + String vmIpWorkers = configs.get(VmIpFetchTaskWorkers.value()); + int vmipwrks = NumbersUtil.parseInt(vmIpWorkers, 10); + + _vmIpFetchExecutor = Executors.newScheduledThreadPool(vmipwrks, new NamedThreadFactory("UserVm-ipfetch")); + String aggregationRange = configs.get("usage.stats.job.aggregation.range"); int _usageAggregationRange = NumbersUtil.parseInt(aggregationRange, 1440); int HOURLY_TIME = 60; @@ -1966,7 +1975,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir String value = _configDao.getValue(Config.SetVmInternalNameUsingDisplayName.key()); _instanceNameFlag = (value == null) ? false : Boolean.parseBoolean(value); - _scaleRetry = NumbersUtil.parseInt(configs.get(Config.ScaleRetry.key()), 2); + _scaleRetry = NumbersUtil.parseInt(configs.get(Config.ScaleRetry.key()), 2); + + _vmIpFetchThreadExecutor = Executors.newFixedThreadPool(VmIpFetchThreadPoolMax.value(), new NamedThreadFactory("vmIpFetchThread")); s_logger.info("User VM Manager is configured."); @@ -1981,7 +1992,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public boolean start() { _executor.scheduleWithFixedDelay(new ExpungeTask(), _expungeInterval, _expungeInterval, TimeUnit.SECONDS); - _executor.scheduleWithFixedDelay(new VmIpFetchTask(), VmIpFetchWaitInterval.value(), VmIpFetchWaitInterval.value(), TimeUnit.SECONDS); + _vmIpFetchExecutor.scheduleWithFixedDelay(new VmIpFetchTask(), VmIpFetchWaitInterval.value(), VmIpFetchWaitInterval.value(), TimeUnit.SECONDS); loadVmDetailsInMapForExternalDhcpIp(); return true; } @@ -2015,6 +2026,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public boolean stop() { _executor.shutdown(); + _vmIpFetchExecutor.shutdown(); return true; } @@ -2599,7 +2611,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId + " corresponding to the vm"); } - return rebootVirtualMachine(CallContext.current().getCallingUserId(), vmId); + UserVm userVm = rebootVirtualMachine(CallContext.current().getCallingUserId(), vmId); + if (userVm != null ) { + // update the vmIdCountMap if the vm is in advanced shared network with out services + final List nics = _nicDao.listByVmId(vmId); + for (NicVO nic : nics) { + Network network = _networkModel.getNetwork(nic.getNetworkId()); + if (_networkModel.isSharedNetworkWithoutServices(network.getId())) { + s_logger.debug("Adding vm " +vmId +" nic id "+ nic.getId() +" into vmIdCountMap as part of vm " + + "reboot for vm ip fetch "); + vmIdCountMap.put(nic.getId(), new VmAndCountDetails(nic.getInstanceId(), VmIpFetchTrialMax.value())); + } + } + return userVm; + } + return null; } @Override @@ -3808,7 +3834,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(); @@ -3896,6 +3922,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } + final VirtualMachineProfile vmProfile = profile; + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + final UserVmVO vm = _vmDao.findById(vmProfile.getId()); + final List nics = _nicDao.listByVmId(vm.getId()); + for (NicVO nic : nics) { + Network network = _networkModel.getNetwork(nic.getNetworkId()); + if (_networkModel.isSharedNetworkWithoutServices(network.getId())) { + vmIdCountMap.put(nic.getId(), new VmAndCountDetails(nic.getInstanceId(), VmIpFetchTrialMax.value())); + } + } + } + }); + return true; } @@ -5835,7 +5876,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {EnableDynamicallyScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax}; + return new ConfigKey[] {EnableDynamicallyScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers}; } @Override diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 22c0b5a9e0a..3cf3ad1744f 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -751,6 +751,13 @@ public class VirtualMachineMO extends BaseMO { return (VirtualMachineConfigInfo)_context.getVimClient().getDynamicProperty(_mor, "config"); } + public boolean isToolsInstallerMounted() throws Exception { + return _context.getVimClient().getDynamicProperty(_mor, "runtime.toolsInstallerMounted"); + } + public GuestInfo getGuestInfo() throws Exception { + return (GuestInfo)_context.getVimClient().getDynamicProperty(_mor, "guest"); + } + public VirtualMachineConfigSummary getConfigSummary() throws Exception { return (VirtualMachineConfigSummary)_context.getVimClient().getDynamicProperty(_mor, "summary.config"); }