diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 78236fabbbe..870c7e7f1f4 100644 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -523,6 +523,7 @@ public class EventTypes { public static final String EVENT_NIC_SECONDARY_IP_ASSIGN = "NIC.SECONDARY.IP.ASSIGN"; public static final String EVENT_NIC_SECONDARY_IP_UNASSIGN = "NIC.SECONDARY.IP.UNASSIGN"; public static final String EVENT_NIC_SECONDARY_IP_CONFIGURE = "NIC.SECONDARY.IP.CONFIGURE"; + public static final String EVENT_NETWORK_EXTERNAL_DHCP_VM_IPFETCH = "EXTERNAL.DHCP.VM.IP.FETCH"; //Usage related events public static final String EVENT_USAGE_REMOVE_USAGE_RECORDS = "USAGE.REMOVE.USAGE.RECORDS"; diff --git a/core/src/com/cloud/agent/api/GetVmIpAddressCommand.java b/core/src/com/cloud/agent/api/GetVmIpAddressCommand.java new file mode 100644 index 00000000000..a9c7413b9f0 --- /dev/null +++ b/core/src/com/cloud/agent/api/GetVmIpAddressCommand.java @@ -0,0 +1,50 @@ +// +// 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 GetVmIpAddressCommand extends Command { + + String vmName; + String vmNetworkCidr; + boolean windows = false; + + public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows) { + this.vmName = vmName; + this.windows = windows; + this.vmNetworkCidr = vmNetworkCidr; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public String getVmName(){ + return vmName; + } + + public boolean isWindows(){ + return windows; + } + + public String getVmNetworkCidr() { + return vmNetworkCidr; + } +} diff --git a/core/src/com/cloud/agent/api/routing/VmDataCommand.java b/core/src/com/cloud/agent/api/routing/VmDataCommand.java index 733866c066c..c74c7cf7d88 100644 --- a/core/src/com/cloud/agent/api/routing/VmDataCommand.java +++ b/core/src/com/cloud/agent/api/routing/VmDataCommand.java @@ -56,6 +56,11 @@ public class VmDataCommand extends NetworkElementCommand { this.executeInSequence = executeInSequence; } + public VmDataCommand(String vmName) { + this.vmName = vmName; + this.vmData = new ArrayList(); + } + public String getVmIpAddress() { return vmIpAddress; } diff --git a/engine/schema/src/com/cloud/network/dao/NetworkDao.java b/engine/schema/src/com/cloud/network/dao/NetworkDao.java index 037f776d5cf..49c801558e6 100644 --- a/engine/schema/src/com/cloud/network/dao/NetworkDao.java +++ b/engine/schema/src/com/cloud/network/dao/NetworkDao.java @@ -31,6 +31,8 @@ public interface NetworkDao extends GenericDao, StateDao listByOwner(long ownerId); + List listByGuestType(GuestType type); + List listBy(long accountId, long offeringId, long dataCenterId); List listBy(long accountId, long dataCenterId, String cidr, boolean skipVpc); diff --git a/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java index 433ededef2a..3c799851375 100644 --- a/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java +++ b/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java @@ -270,6 +270,14 @@ public class NetworkDaoImpl extends GenericDaoBase implements N return listBy(sc, null); } + @Override + public List listByGuestType(Network.GuestType type) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("guestType", type); + return listBy(sc, null); + } + + public List findBy(final TrafficType trafficType, final Mode mode, final BroadcastDomainType broadcastType, final long networkOfferingId, final long dataCenterId) { final SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("trafficType", trafficType); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index a413d1131d6..1661390baab 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -27,8 +27,10 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import javax.ejb.Local; @@ -97,6 +99,7 @@ import com.cloud.agent.api.GetVmDiskStatsCommand; import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.PvlanSetupCommand; +import com.cloud.agent.api.GetVmIpAddressCommand; import com.cloud.agent.api.StartAnswer; import com.cloud.agent.api.VmDiskStatsEntry; import com.cloud.agent.api.VmStatsEntry; @@ -129,9 +132,12 @@ import com.cloud.deploy.DeploymentPlanningManager; import com.cloud.deploy.PlannerHostReservationVO; import com.cloud.deploy.dao.PlannerHostReservationDao; import com.cloud.domain.DomainVO; +import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; +import com.cloud.event.ActionEventUtils; +import com.cloud.event.ActionEventUtils; import com.cloud.event.UsageEventUtils; import com.cloud.event.UsageEventVO; import com.cloud.event.dao.UsageEventDao; @@ -475,11 +481,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir protected int _expungeDelay; protected boolean _dailyOrHourly = false; private int capacityReleaseInterval; + ExecutorService _vmIpFetchThreadExecutor; + protected String _instance; protected String _zone; protected boolean _instanceNameFlag; protected int _scaleRetry; + protected Map vmIdCountMap = new ConcurrentHashMap<>(); @Inject ConfigurationDao _configDao; @@ -496,6 +505,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject ManagementService _mgr; + static final ConfigKey VmIpFetchWaitInterval = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmip.retrieval.interval", "180", + "Wait Interval (in seconds) for shared network vm dhcp ip addr fetch for next iteration ", true); + + static final ConfigKey VmIpFetchTrialMax = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmip.max.retry", "10", + "The max number of retrieval times for shared entwork vm dhcp ip fetch, in case of failures", true); + + static final ConfigKey VmIpFetchThreadPoolMax = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmipFetch.threadPool.max", "10", + "number of threads for fetching vms ip address", true); + + @Override public UserVmVO getVirtualMachine(long vmId) { return _vmDao.findById(vmId); @@ -524,6 +543,122 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.memory, displayVm, memory); } + public class VmAndCountDetails { + long vmId; + int retrievalCount = VmIpFetchTrialMax.value(); + + + public VmAndCountDetails() { + } + + public VmAndCountDetails (long vmId, int retrievalCount) { + this.vmId = vmId; + this.retrievalCount = retrievalCount; + } + + public VmAndCountDetails (long vmId) { + this.vmId = vmId; + } + + public int getRetrievalCount() { + return retrievalCount; + } + + public void setRetrievalCount(int retrievalCount) { + this.retrievalCount = retrievalCount; + } + + public long getVmId() { + return vmId; + } + + public void setVmId(long vmId) { + this.vmId = vmId; + } + + public void decrementCount() { + this.retrievalCount--; + + } + } + + protected class VmIpAddrFetchThread extends ManagedContextRunnable { + + + long nicId; + long vmId; + String vmName; + boolean isWindows; + Long hostId; + String networkCidr; + + public VmIpAddrFetchThread() { + } + + public VmIpAddrFetchThread(long vmId, long nicId, String instanceName, boolean windows, Long hostId, String networkCidr) { + this.vmId = vmId; + this.nicId = nicId; + this.vmName = instanceName; + this.isWindows = windows; + this.hostId = hostId; + this.networkCidr = networkCidr; + } + + @Override + protected void runInContext() { + GetVmIpAddressCommand cmd = new GetVmIpAddressCommand(vmName, networkCidr, isWindows); + boolean decrementCount = true; + + try { + s_logger.debug("Trying for vm "+ vmId +" nic Id "+nicId +" ip retrieval ..."); + Answer answer = _agentMgr.send(hostId, cmd); + NicVO nic = _nicDao.findById(nicId); + if (answer.getResult()) { + String vmIp = answer.getDetails(); + + if (NetUtils.isValidIp(vmIp)) { + // set this vm ip addr in vm nic. + if (nic != null) { + nic.setIp4Address(vmIp); + _nicDao.update(nicId, nic); + s_logger.debug("Vm "+ vmId +" IP "+vmIp +" got retrieved successfully"); + vmIdCountMap.remove(nicId); + decrementCount = false; + ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, + Domain.ROOT_DOMAIN, EventTypes.EVENT_NETWORK_EXTERNAL_DHCP_VM_IPFETCH, + "VM " + vmId + " nic id " + nicId + " ip address " + vmIp + " got fetched successfully"); + } + } + } else { + //previously vm has ip and nic table has ip address. After vm restart or stop/start + //if vm doesnot get the ip then set the ip in nic table to null + if (nic.getIp4Address() != null) { + nic.setIp4Address(null); + _nicDao.update(nicId, nic); + } + if (answer.getDetails() != null) { + s_logger.debug("Failed to get vm ip for Vm "+ vmId + answer.getDetails()); + } + } + } catch (OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + } catch (AgentUnavailableException e) { + s_logger.warn("Agent Unavailable ", e); + } finally { + if (decrementCount) { + VmAndCountDetails vmAndCount = vmIdCountMap.get(nicId); + vmAndCount.decrementCount(); + s_logger.debug("Ip is not retrieved for VM " + vmId +" nic "+nicId + " ... decremented count to "+vmAndCount.getRetrievalCount()); + vmIdCountMap.put(nicId, vmAndCount); + } + } + } + } + + + + + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_RESETPASSWORD, eventDescription = "resetting Vm password", async = true) public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) throws ResourceUnavailableException, InsufficientCapacityException { @@ -1716,9 +1851,37 @@ 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); + loadVmDetailsInMapForExternalDhcpIp(); return true; } + private void loadVmDetailsInMapForExternalDhcpIp() { + + List networks = _networkDao.listByGuestType(Network.GuestType.Shared); + + for (NetworkVO network: networks) { + if(_networkModel.isSharedNetworkWithoutServices(network.getId())) { + List nics = _nicDao.listByNetworkId(network.getId()); + + for (NicVO nic : nics) { + + if (nic.getIp4Address() == null) { + long nicId = nic.getId(); + long vmId = nic.getInstanceId(); + VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); + + // only load running vms. For stopped vms get loaded on starting + if (vmInstance.getState() == State.Running) { + VmAndCountDetails vmAndCount = new VmAndCountDetails(vmId, VmIpFetchTrialMax.value()); + vmIdCountMap.put(nicId, vmAndCount); + } + } + } + } + } + } + @Override public boolean stop() { _executor.shutdown(); @@ -1872,6 +2035,66 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } + + + protected class VmIpFetchTask extends ManagedContextRunnable { + + public VmIpFetchTask() { + GlobalLock scanLock = GlobalLock.getInternLock("vmIpFetch"); + } + + @Override + protected void runInContext() { + GlobalLock scanLock = GlobalLock.getInternLock("vmIpFetch"); + + try { + if (scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { + try { + + for (Entry entry: vmIdCountMap.entrySet()) { + long nicId = entry.getKey(); + VmAndCountDetails vmIdAndCount = entry.getValue(); + long vmId = vmIdAndCount.getVmId(); + + if (vmIdAndCount.getRetrievalCount() <= 0) { + vmIdCountMap.remove(nicId); + s_logger.debug("Vm " + vmId +" nic "+nicId + " count is zero .. removing vm nic from map "); + + ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, + Domain.ROOT_DOMAIN, EventTypes.EVENT_NETWORK_EXTERNAL_DHCP_VM_IPFETCH, + "VM " + vmId + " nic id "+ nicId + " ip addr fetch failed "); + + continue; + } + + + UserVm userVm = _vmDao.findById(vmId); + VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); + NicVO nicVo = _nicDao.findById(nicId); + NetworkVO network = _networkDao.findById(nicVo.getNetworkId()); + + VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(userVm); + VirtualMachine vm = vmProfile.getVirtualMachine(); + boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); + + _vmIpFetchThreadExecutor.execute(new VmIpAddrFetchThread(vmId, nicId, vmInstance.getInstanceName(), + isWindows, vm.getHostId(), network.getCidr())); + + } + } catch (Exception e) { + s_logger.error("Caught the Exception in VmIpFetchTask", e); + } finally { + scanLock.unlock(); + } + } + } finally { + scanLock.releaseRef(); + } + + } + } + + protected class ExpungeTask extends ManagedContextRunnable { public ExpungeTask() { } @@ -5045,7 +5268,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {EnableDynamicallyScaleVm}; + return new ConfigKey[] {EnableDynamicallyScaleVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax}; } @Override diff --git a/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java b/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java index cf4fc35cf82..89f15d58b9c 100644 --- a/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java +++ b/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java @@ -45,6 +45,11 @@ public class MockNetworkDaoImpl extends GenericDaoBase implemen return null; } + @Override + public List listByGuestType(GuestType type) { + return null; + } + /* (non-Javadoc) * @see com.cloud.network.dao.NetworkDao#listBy(long, long, long) */