/** * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. * * This software is licensed under the GNU General Public License v3 or later. * * It is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ package com.cloud.resource; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager.TapAgentsAction; import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.MaintainCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.agent.api.UpdateHostPasswordCommand; import com.cloud.agent.manager.AgentAttache; import com.cloud.agent.transport.Request; import com.cloud.api.ApiConstants; import com.cloud.api.commands.AddClusterCmd; import com.cloud.api.commands.AddHostCmd; import com.cloud.api.commands.AddSecondaryStorageCmd; import com.cloud.api.commands.CancelMaintenanceCmd; import com.cloud.api.commands.DeleteClusterCmd; import com.cloud.api.commands.PrepareForMaintenanceCmd; import com.cloud.api.commands.ReconnectHostCmd; import com.cloud.api.commands.UpdateHostCmd; import com.cloud.api.commands.UpdateHostPasswordCmd; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; import com.cloud.cluster.ClusterManager; import com.cloud.cluster.ManagementServerNode; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenterIpAddressVO; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterIpAddressDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.DiscoveryException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.ha.HighAvailabilityManager; import com.cloud.ha.HighAvailabilityManager.WorkType; import com.cloud.host.DetailVO; import com.cloud.host.Host; import com.cloud.host.Host.HostAllocationState; import com.cloud.host.Host.Type; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostDetailsDao; import com.cloud.host.dao.HostTagsDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.kvm.resource.KvmDummyResourceBase; import com.cloud.network.IPAddressVO; import com.cloud.network.dao.IPAddressDao; import com.cloud.org.Cluster; import com.cloud.org.Grouping; import com.cloud.org.Managed; import com.cloud.resource.ResourceState.Event; import com.cloud.storage.GuestOSCategoryVO; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.StorageService; import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.StoragePoolDao; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.utils.Pair; import com.cloud.utils.UriUtils; import com.cloud.utils.component.Adapters; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.VMInstanceDao; @Local({ ResourceManager.class, ResourceService.class }) public class ResourceManagerImpl implements ResourceManager, ResourceService, Manager { private static final Logger s_logger = Logger.getLogger(ResourceManagerImpl.class); private String _name; @Inject AccountManager _accountMgr; @Inject AgentManager _agentMgr; @Inject StorageManager _storageMgr; @Inject protected SecondaryStorageVmManager _secondaryStorageMgr; @Inject protected DataCenterDao _dcDao; @Inject protected HostPodDao _podDao; @Inject protected ClusterDetailsDao _clusterDetailsDao; @Inject protected ClusterDao _clusterDao; @Inject protected HostDao _hostDao; @Inject protected HostDetailsDao _hostDetailsDao; @Inject protected HostTagsDao _hostTagsDao; @Inject protected GuestOSCategoryDao _guestOSCategoryDao; @Inject protected DataCenterIpAddressDao _privateIPAddressDao; @Inject protected IPAddressDao _publicIPAddressDao; @Inject protected VirtualMachineManager _vmMgr; @Inject protected VMInstanceDao _vmDao; @Inject protected HighAvailabilityManager _haMgr; @Inject protected StorageService _storageSvr; @Inject(adapter = Discoverer.class) protected Adapters _discoverers; @Inject protected ClusterManager _clusterMgr; @Inject protected StoragePoolHostDao _storagePoolHostDao; @Inject protected StoragePoolDao _storagePoolDao; @Inject protected CapacityDao _capacityDao; @Inject protected HostDetailsDao _detailsDao; protected long _nodeId = ManagementServerNode.getManagementServerId(); protected HashMap _resourceStateAdapters = new HashMap(); protected HashMap> _lifeCycleListeners = new HashMap>(); private void insertListener(Integer event, ResourceListener listener) { List lst = _lifeCycleListeners.get(event); if (lst == null) { lst = new ArrayList(); _lifeCycleListeners.put(event, lst); } if (lst.contains(listener)) { throw new CloudRuntimeException("Duplicate resource lisener:" + listener.getClass().getSimpleName()); } lst.add(listener); } @Override public void registerResourceEvent(Integer event, ResourceListener listener) { synchronized (_lifeCycleListeners) { if ((event & ResourceListener.EVENT_DISCOVER_BEFORE) != 0) { insertListener(ResourceListener.EVENT_DISCOVER_BEFORE, listener); } if ((event & ResourceListener.EVENT_DISCOVER_AFTER) != 0) { insertListener(ResourceListener.EVENT_DISCOVER_AFTER, listener); } if ((event & ResourceListener.EVENT_DELETE_HOST_BEFORE) != 0) { insertListener(ResourceListener.EVENT_DELETE_HOST_BEFORE, listener); } if ((event & ResourceListener.EVENT_DELETE_HOST_AFTER) != 0) { insertListener(ResourceListener.EVENT_DELETE_HOST_AFTER, listener); } if ((event & ResourceListener.EVENT_CANCEL_MAINTENANCE_BEFORE) != 0) { insertListener(ResourceListener.EVENT_CANCEL_MAINTENANCE_BEFORE, listener); } if ((event & ResourceListener.EVENT_CANCEL_MAINTENANCE_AFTER) != 0) { insertListener(ResourceListener.EVENT_CANCEL_MAINTENANCE_AFTER, listener); } if ((event & ResourceListener.EVENT_PREPARE_MAINTENANCE_BEFORE) != 0) { insertListener(ResourceListener.EVENT_PREPARE_MAINTENANCE_BEFORE, listener); } if ((event & ResourceListener.EVENT_PREPARE_MAINTENANCE_AFTER) != 0) { insertListener(ResourceListener.EVENT_PREPARE_MAINTENANCE_AFTER, listener); } } } @Override public void unregisterResourceEvent(ResourceListener listener) { synchronized (_lifeCycleListeners) { Iterator it = _lifeCycleListeners.entrySet().iterator(); while (it.hasNext()) { Map.Entry> items = (Map.Entry>)it.next(); List lst = items.getValue(); lst.remove(listener); } } } protected void processResourceEvent(Integer event, Object...params) { List lst = _lifeCycleListeners.get(event); if (lst == null || lst.size() == 0) { return; } String eventName; for (ResourceListener l : lst) { if (event == ResourceListener.EVENT_DISCOVER_BEFORE) { l.processDiscoverEventBefore((Long) params[0], (Long) params[1], (Long) params[2], (URI) params[3], (String) params[4], (String) params[5], (List) params[6]); eventName = "EVENT_DISCOVER_BEFORE"; } else if (event == ResourceListener.EVENT_DISCOVER_AFTER) { l.processDiscoverEventAfter((Map>) params[0]); eventName = "EVENT_DISCOVER_AFTER"; } else if (event == ResourceListener.EVENT_DELETE_HOST_BEFORE) { l.processDeleteHostEventBefore((HostVO) params[0]); eventName = "EVENT_DELETE_HOST_BEFORE"; } else if (event == ResourceListener.EVENT_DELETE_HOST_AFTER) { l.processDeletHostEventAfter((HostVO) params[0]); eventName = "EVENT_DELETE_HOST_AFTER"; } else if (event == ResourceListener.EVENT_CANCEL_MAINTENANCE_BEFORE) { l.processCancelMaintenaceEventBefore((Long) params[0]); eventName = "EVENT_CANCEL_MAINTENANCE_BEFORE"; } else if (event == ResourceListener.EVENT_CANCEL_MAINTENANCE_AFTER) { l.processCancelMaintenaceEventAfter((Long) params[0]); eventName = "EVENT_CANCEL_MAINTENANCE_AFTER"; } else if (event == ResourceListener.EVENT_PREPARE_MAINTENANCE_BEFORE) { l.processPrepareMaintenaceEventBefore((Long) params[0]); eventName = "EVENT_PREPARE_MAINTENANCE_BEFORE"; } else if (event == ResourceListener.EVENT_PREPARE_MAINTENANCE_AFTER) { l.processPrepareMaintenaceEventAfter((Long) params[0]); eventName = "EVENT_PREPARE_MAINTENANCE_AFTER"; } else { throw new CloudRuntimeException("Unknown resource event:" + event); } s_logger.debug("Sent resource event " + eventName + " to listener " + l.getClass().getSimpleName()); } } @Override public List discoverCluster(AddClusterCmd cmd) throws IllegalArgumentException, DiscoveryException { Long dcId = cmd.getZoneId(); Long podId = cmd.getPodId(); String clusterName = cmd.getClusterName(); String url = cmd.getUrl(); String username = cmd.getUsername(); String password = cmd.getPassword(); if (url != null) { url = URLDecoder.decode(url); } URI uri = null; // Check if the zone exists in the system DataCenterVO zone = _dcDao.findById(dcId); if (zone == null) { throw new InvalidParameterValueException("Can't find zone by id " + dcId); } Account account = UserContext.current().getCaller(); if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(account.getType())) { throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + dcId); } // Check if the pod exists in the system if (podId != null) { if (_podDao.findById(podId) == null) { throw new InvalidParameterValueException("Can't find pod by id " + podId); } // check if pod belongs to the zone HostPodVO pod = _podDao.findById(podId); if (!Long.valueOf(pod.getDataCenterId()).equals(dcId)) { throw new InvalidParameterValueException("Pod " + podId + " doesn't belong to the zone " + dcId); } } // Verify cluster information and create a new cluster if needed if (clusterName == null || clusterName.isEmpty()) { throw new InvalidParameterValueException("Please specify cluster name"); } if (cmd.getHypervisor() == null || cmd.getHypervisor().isEmpty()) { throw new InvalidParameterValueException("Please specify a hypervisor"); } Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.getType(cmd.getHypervisor()); if (hypervisorType == null) { s_logger.error("Unable to resolve " + cmd.getHypervisor() + " to a valid supported hypervisor type"); throw new InvalidParameterValueException("Unable to resolve " + cmd.getHypervisor() + " to a supported "); } Cluster.ClusterType clusterType = null; if (cmd.getClusterType() != null && !cmd.getClusterType().isEmpty()) { clusterType = Cluster.ClusterType.valueOf(cmd.getClusterType()); } if (clusterType == null) { clusterType = Cluster.ClusterType.CloudManaged; } Grouping.AllocationState allocationState = null; if (cmd.getAllocationState() != null && !cmd.getAllocationState().isEmpty()) { try { allocationState = Grouping.AllocationState.valueOf(cmd.getAllocationState()); } catch (IllegalArgumentException ex) { throw new InvalidParameterValueException("Unable to resolve Allocation State '" + cmd.getAllocationState() + "' to a supported state"); } } if (allocationState == null) { allocationState = Grouping.AllocationState.Enabled; } Discoverer discoverer = getMatchingDiscover(hypervisorType); if (discoverer == null) { throw new InvalidParameterValueException("Could not find corresponding resource manager for " + cmd.getHypervisor()); } List result = new ArrayList(); long clusterId = 0; ClusterVO cluster = new ClusterVO(dcId, podId, clusterName); cluster.setHypervisorType(cmd.getHypervisor()); cluster.setClusterType(clusterType); cluster.setAllocationState(allocationState); try { cluster = _clusterDao.persist(cluster); } catch (Exception e) { // no longer tolerate exception during the cluster creation phase throw new CloudRuntimeException("Unable to create cluster " + clusterName + " in pod " + podId + " and data center " + dcId, e); } clusterId = cluster.getId(); result.add(cluster); if (clusterType == Cluster.ClusterType.CloudManaged) { return result; } // save cluster details for later cluster/host cross-checking Map details = new HashMap(); details.put("url", url); details.put("username", username); details.put("password", password); _clusterDetailsDao.persist(cluster.getId(), details); boolean success = false; try { try { uri = new URI(UriUtils.encodeURIComponent(url)); if (uri.getScheme() == null) { throw new InvalidParameterValueException("uri.scheme is null " + url + ", add http:// as a prefix"); } else if (uri.getScheme().equalsIgnoreCase("http")) { if (uri.getHost() == null || uri.getHost().equalsIgnoreCase("") || uri.getPath() == null || uri.getPath().equalsIgnoreCase("")) { throw new InvalidParameterValueException("Your host and/or path is wrong. Make sure it's of the format http://hostname/path"); } } } catch (URISyntaxException e) { throw new InvalidParameterValueException(url + " is not a valid uri"); } List hosts = new ArrayList(); Map> resources = null; resources = discoverer.find(dcId, podId, clusterId, uri, username, password, null); if (resources != null) { for (Map.Entry> entry : resources.entrySet()) { ServerResource resource = entry.getKey(); // For Hyper-V, we are here means agent have already started and connected to management server if (hypervisorType == Hypervisor.HypervisorType.Hyperv) { break; } HostVO host = (HostVO)createHostAndAgent(resource, entry.getValue(), true, null, null, false); if (host != null) { hosts.add(host); } discoverer.postDiscovery(hosts, _nodeId); } s_logger.info("External cluster has been successfully discovered by " + discoverer.getName()); success = true; return result; } s_logger.warn("Unable to find the server resources at " + url); throw new DiscoveryException("Unable to add the external cluster"); } finally { if (!success) { _clusterDetailsDao.deleteDetails(clusterId); _clusterDao.remove(clusterId); } } } private Discoverer getMatchingDiscover(Hypervisor.HypervisorType hypervisorType) { Enumeration en = _discoverers.enumeration(); while (en.hasMoreElements()) { Discoverer discoverer = en.nextElement(); if (discoverer.getHypervisorType() == hypervisorType) { return discoverer; } } return null; } @Override public List discoverHosts(AddHostCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException { Long dcId = cmd.getZoneId(); Long podId = cmd.getPodId(); Long clusterId = cmd.getClusterId(); String clusterName = cmd.getClusterName(); String url = cmd.getUrl(); String username = cmd.getUsername(); String password = cmd.getPassword(); List hostTags = cmd.getHostTags(); dcId = _accountMgr.checkAccessAndSpecifyAuthority(UserContext.current().getCaller(), dcId); // this is for standalone option if (clusterName == null && clusterId == null) { clusterName = "Standalone-" + url; } if (clusterId != null) { ClusterVO cluster = _clusterDao.findById(clusterId); if (cluster == null) { throw new InvalidParameterValueException("can not fine cluster for clusterId " + clusterId); } else { if (cluster.getGuid() == null) { List hosts = _hostDao.listByCluster(clusterId); if (!hosts.isEmpty()) { throw new CloudRuntimeException("Guid is not updated for cluster " + clusterId + " need to wait hosts in this cluster up"); } } } } String allocationState = cmd.getAllocationState(); if (allocationState == null) { allocationState = Host.HostAllocationState.Enabled.toString(); } return discoverHostsFull(dcId, podId, clusterId, clusterName, url, username, password, cmd.getHypervisor(), hostTags, cmd.getFullUrlParams(), allocationState); } @Override public List discoverHosts(AddSecondaryStorageCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException { Long dcId = cmd.getZoneId(); String url = cmd.getUrl(); return discoverHostsFull(dcId, null, null, null, url, null, null, "SecondaryStorage", null, null, null); } private List discoverHostsFull(Long dcId, Long podId, Long clusterId, String clusterName, String url, String username, String password, String hypervisorType, List hostTags, Map params, String allocationState) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException { URI uri = null; // Check if the zone exists in the system DataCenterVO zone = _dcDao.findById(dcId); if (zone == null) { throw new InvalidParameterValueException("Can't find zone by id " + dcId); } Account account = UserContext.current().getCaller(); if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(account.getType())) { throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + dcId); } // Check if the pod exists in the system if (podId != null) { if (_podDao.findById(podId) == null) { throw new InvalidParameterValueException("Can't find pod by id " + podId); } // check if pod belongs to the zone HostPodVO pod = _podDao.findById(podId); if (!Long.valueOf(pod.getDataCenterId()).equals(dcId)) { throw new InvalidParameterValueException("Pod " + podId + " doesn't belong to the zone " + dcId); } } // Verify cluster information and create a new cluster if needed if (clusterName != null && clusterId != null) { throw new InvalidParameterValueException("Can't specify cluster by both id and name"); } if (hypervisorType == null || hypervisorType.isEmpty()) { throw new InvalidParameterValueException("Need to specify Hypervisor Type"); } if ((clusterName != null || clusterId != null) && podId == null) { throw new InvalidParameterValueException("Can't specify cluster without specifying the pod"); } if (clusterId != null) { if (_clusterDao.findById(clusterId) == null) { throw new InvalidParameterValueException("Can't find cluster by id " + clusterId); } if(hypervisorType.equalsIgnoreCase(HypervisorType.VMware.toString())) { // VMware only allows adding host to an existing cluster, as we already have a lot of information // in cluster object, to simplify user input, we will construct neccessary information here Map clusterDetails = this._clusterDetailsDao.findDetails(clusterId); username = clusterDetails.get("username"); assert(username != null); password = clusterDetails.get("password"); assert(password != null); try { uri = new URI(UriUtils.encodeURIComponent(url)); url = clusterDetails.get("url") + "/" + uri.getHost(); } catch (URISyntaxException e) { throw new InvalidParameterValueException(url + " is not a valid uri"); } } } if (clusterName != null) { ClusterVO cluster = new ClusterVO(dcId, podId, clusterName); cluster.setHypervisorType(hypervisorType); try { cluster = _clusterDao.persist(cluster); } catch (Exception e) { cluster = _clusterDao.findBy(clusterName, podId); if (cluster == null) { throw new CloudRuntimeException("Unable to create cluster " + clusterName + " in pod " + podId + " and data center " + dcId, e); } } clusterId = cluster.getId(); } try { uri = new URI(UriUtils.encodeURIComponent(url)); if (uri.getScheme() == null) { throw new InvalidParameterValueException("uri.scheme is null " + url + ", add nfs:// as a prefix"); } else if (uri.getScheme().equalsIgnoreCase("nfs")) { if (uri.getHost() == null || uri.getHost().equalsIgnoreCase("") || uri.getPath() == null || uri.getPath().equalsIgnoreCase("")) { throw new InvalidParameterValueException("Your host and/or path is wrong. Make sure it's of the format nfs://hostname/path"); } } } catch (URISyntaxException e) { throw new InvalidParameterValueException(url + " is not a valid uri"); } List hosts = new ArrayList(); s_logger.info("Trying to add a new host at " + url + " in data center " + dcId); Enumeration en = _discoverers.enumeration(); boolean isHypervisorTypeSupported = false; while (en.hasMoreElements()) { Discoverer discoverer = en.nextElement(); if (params != null) { discoverer.putParam(params); } if (!discoverer.matchHypervisor(hypervisorType)) { continue; } isHypervisorTypeSupported = true; Map> resources = null; processResourceEvent(ResourceListener.EVENT_DISCOVER_BEFORE, dcId, podId, clusterId, uri, username, password, hostTags); try { resources = discoverer.find(dcId, podId, clusterId, uri, username, password, hostTags); } catch(DiscoveryException e) { throw e; } catch (Exception e) { s_logger.info("Exception in host discovery process with discoverer: " + discoverer.getName() + ", skip to another discoverer if there is any"); } processResourceEvent(ResourceListener.EVENT_DISCOVER_AFTER, resources); if (resources != null) { for (Map.Entry> entry : resources.entrySet()) { ServerResource resource = entry.getKey(); /* * For KVM, if we go to here, that means kvm agent is already connected to mgt svr. */ if (resource instanceof KvmDummyResourceBase) { Map details = entry.getValue(); String guid = details.get("guid"); List kvmHosts = _hostDao.listBy(Host.Type.Routing, clusterId, podId, dcId); for (HostVO host : kvmHosts) { if (host.getGuid().equalsIgnoreCase(guid)) { if(hostTags != null){ if(s_logger.isTraceEnabled()){ s_logger.trace("Adding Host Tags for KVM host, tags: :"+hostTags); } _hostTagsDao.persist(host.getId(), hostTags); } hosts.add(host); return hosts; } } return null; } HostVO host = (HostVO)createHostAndAgent(resource, entry.getValue(), true, hostTags, allocationState, false); if (host != null) { hosts.add(host); } discoverer.postDiscovery(hosts, _nodeId); } s_logger.info("server resources successfully discovered by " + discoverer.getName()); return hosts; } } if (!isHypervisorTypeSupported) { String msg = "Do not support HypervisorType " + hypervisorType + " for " + url; s_logger.warn(msg); throw new DiscoveryException(msg); } s_logger.warn("Unable to find the server resources at " + url); throw new DiscoveryException("Unable to add the host"); } @Override public Host getHost(long hostId) { return _hostDao.findById(hostId); } @DB private boolean doDeleteHost(long hostId, boolean isForced, boolean isForceDeleteStorage) { User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); // Verify that host exists HostVO host = _hostDao.findById(hostId); if (host == null) { throw new InvalidParameterValueException("Host with id " + hostId + " doesn't exist"); } _accountMgr.checkAccessAndSpecifyAuthority(UserContext.current().getCaller(), host.getDataCenterId()); /* * TODO: check current agent status and updateAgentStatus to removed. If it was already removed, that means * someone is deleting host concurrently, return. And consider the situation of CloudStack shutdown during delete. * A global lock? * */ AgentAttache attache = _agentMgr.findAttache(hostId); // Get storage pool host mappings here because they can be removed as a part of handleDisconnect later //TODO: find out the bad boy, what's a buggy logic! List pools = _storagePoolHostDao.listByHostIdIncludingRemoved(hostId); ResourceStateAdapter.DeleteHostAnswer answer = (ResourceStateAdapter.DeleteHostAnswer) dispatchToStateAdapters(ResourceStateAdapter.Event.DELETE_HOST, false, host, new Boolean(isForced), new Boolean(isForceDeleteStorage)); if (answer.getIsException()) { return false; } if (!answer.getIsContinue()) { return true; } Transaction txn = Transaction.currentTxn(); txn.start(); _dcDao.releasePrivateIpAddress(host.getPrivateIpAddress(), host.getDataCenterId(), null); _agentMgr.disconnectWithoutInvestigation(hostId, Status.Event.Remove); // delete host details _hostDetailsDao.deleteDetails(hostId); host.setGuid(null); Long clusterId = host.getClusterId(); host.setClusterId(null); _hostDao.update(host.getId(), host); _hostDao.remove(hostId); if (clusterId != null) { List hosts = _hostDao.listByCluster(clusterId); if (hosts.size() == 0) { ClusterVO cluster = _clusterDao.findById(clusterId); cluster.setGuid(null); _clusterDao.update(clusterId, cluster); } } try { updateResourceState(host, ResourceState.Event.DeleteHost, _nodeId); } catch (NoTransitionException e) { s_logger.debug("Cannot transmit host " + host.getId() + "to Enabled state", e); } // Delete the associated entries in host ref table _storagePoolHostDao.deletePrimaryRecordsForHost(hostId); // For pool ids you got, delete local storage host entries in pool table where for (StoragePoolHostVO pool : pools) { Long poolId = pool.getPoolId(); StoragePoolVO storagePool = _storagePoolDao.findById(poolId); if (storagePool.isLocal() && isForceDeleteStorage) { storagePool.setUuid(null); storagePool.setClusterId(null); _storagePoolDao.update(poolId, storagePool); _storagePoolDao.remove(poolId); s_logger.debug("Local storage id=" + poolId + " is removed as a part of host removal id=" + hostId); } } // delete the op_host_capacity entry Object[] capacityTypes = { Capacity.CAPACITY_TYPE_CPU, Capacity.CAPACITY_TYPE_MEMORY }; SearchCriteria hostCapacitySC = _capacityDao.createSearchCriteria(); hostCapacitySC.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, hostId); hostCapacitySC.addAnd("capacityType", SearchCriteria.Op.IN, capacityTypes); _capacityDao.remove(hostCapacitySC); txn.commit(); return true; } @Override public boolean deleteHost(long hostId, boolean isForced, boolean isForceDeleteStorage) { try { Boolean result = _clusterMgr.propagateResourceEvent(hostId, ResourceState.Event.DeleteHost); if (result != null) { return result; } } catch (AgentUnavailableException e) { return false; } return doDeleteHost(hostId, isForced, isForceDeleteStorage); } @Override @DB public boolean deleteCluster(DeleteClusterCmd cmd) { Transaction txn = Transaction.currentTxn(); try { txn.start(); ClusterVO cluster = _clusterDao.lockRow(cmd.getId(), true); if (cluster == null) { if (s_logger.isDebugEnabled()) { s_logger.debug("Cluster: " + cmd.getId() + " does not even exist. Delete call is ignored."); } txn.rollback(); return true; } List hosts = _hostDao.listByCluster(cmd.getId()); if (hosts.size() > 0) { if (s_logger.isDebugEnabled()) { s_logger.debug("Cluster: " + cmd.getId() + " still has hosts"); } txn.rollback(); return false; } _clusterDao.remove(cmd.getId()); txn.commit(); return true; } catch (Throwable t) { s_logger.error("Unable to delete cluster: " + cmd.getId(), t); txn.rollback(); return false; } } @Override @DB public Cluster updateCluster(Cluster clusterToUpdate, String clusterType, String hypervisor, String allocationState, String managedstate) { ClusterVO cluster = (ClusterVO) clusterToUpdate; // Verify cluster information and update the cluster if needed boolean doUpdate = false; if (hypervisor != null && !hypervisor.isEmpty()) { Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.getType(hypervisor); if (hypervisorType == null) { s_logger.error("Unable to resolve " + hypervisor + " to a valid supported hypervisor type"); throw new InvalidParameterValueException("Unable to resolve " + hypervisor + " to a supported type"); } else { cluster.setHypervisorType(hypervisor); doUpdate = true; } } Cluster.ClusterType newClusterType = null; if (clusterType != null && !clusterType.isEmpty()) { try { newClusterType = Cluster.ClusterType.valueOf(clusterType); } catch (IllegalArgumentException ex) { throw new InvalidParameterValueException("Unable to resolve " + clusterType + " to a supported type"); } if (newClusterType == null) { s_logger.error("Unable to resolve " + clusterType + " to a valid supported cluster type"); throw new InvalidParameterValueException("Unable to resolve " + clusterType + " to a supported type"); } else { cluster.setClusterType(newClusterType); doUpdate = true; } } Grouping.AllocationState newAllocationState = null; if (allocationState != null && !allocationState.isEmpty()) { try { newAllocationState = Grouping.AllocationState.valueOf(allocationState); } catch (IllegalArgumentException ex) { throw new InvalidParameterValueException("Unable to resolve Allocation State '" + allocationState + "' to a supported state"); } if (newAllocationState == null) { s_logger.error("Unable to resolve " + allocationState + " to a valid supported allocation State"); throw new InvalidParameterValueException("Unable to resolve " + allocationState + " to a supported state"); } else { cluster.setAllocationState(newAllocationState); doUpdate = true; } } Managed.ManagedState newManagedState = null; Managed.ManagedState oldManagedState = cluster.getManagedState(); if (managedstate != null && !managedstate.isEmpty()) { try { newManagedState = Managed.ManagedState.valueOf(managedstate); } catch (IllegalArgumentException ex) { throw new InvalidParameterValueException("Unable to resolve Managed State '" + managedstate + "' to a supported state"); } if (newManagedState == null) { s_logger.error("Unable to resolve Managed State '" + managedstate + "' to a supported state"); throw new InvalidParameterValueException("Unable to resolve Managed State '" + managedstate + "' to a supported state"); } else { doUpdate = true; } } if (doUpdate) { Transaction txn = Transaction.currentTxn(); try { txn.start(); _clusterDao.update(cluster.getId(), cluster); txn.commit(); } catch (Exception e) { s_logger.error("Unable to update cluster due to " + e.getMessage(), e); throw new CloudRuntimeException("Failed to update cluster. Please contact Cloud Support."); } } if( newManagedState != null && !newManagedState.equals(oldManagedState)) { Transaction txn = Transaction.currentTxn(); if( newManagedState.equals(Managed.ManagedState.Unmanaged) ) { boolean success = false; try { txn.start(); cluster.setManagedState(Managed.ManagedState.PrepareUnmanaged); _clusterDao.update(cluster.getId(), cluster); txn.commit(); List hosts = _hostDao.listBy(Host.Type.Routing, cluster.getId(), cluster.getPodId(), cluster.getDataCenterId()); for( HostVO host : hosts ) { if(host.getType().equals(Host.Type.Routing) && !host.getStatus().equals(Status.Down) && !host.getStatus().equals(Status.Disconnected) && !host.getStatus().equals(Status.Up) && !host.getStatus().equals(Status.Alert) ) { String msg = "host " + host.getPrivateIpAddress() + " should not be in " + host.getStatus().toString() + " status"; throw new CloudRuntimeException("PrepareUnmanaged Failed due to " + msg); } } for( HostVO host : hosts ) { if ( host.getStatus().equals(Status.Up )) { umanageHost(host.getId()); } } int retry = 10; boolean lsuccess = true; for ( int i = 0; i < retry; i++) { lsuccess = true; try { Thread.sleep(20 * 1000); } catch (Exception e) { } hosts = _hostDao.listBy(Host.Type.Routing, cluster.getId(), cluster.getPodId(), cluster.getDataCenterId()); for( HostVO host : hosts ) { if ( !host.getStatus().equals(Status.Down) && !host.getStatus().equals(Status.Disconnected) && !host.getStatus().equals(Status.Alert)) { lsuccess = false; break; } } if( lsuccess == true ) { success = true; break; } } if ( success == false ) { throw new CloudRuntimeException("PrepareUnmanaged Failed due to some hosts are still in UP status after 5 Minutes, please try later "); } } finally { txn.start(); cluster.setManagedState(success? Managed.ManagedState.Unmanaged : Managed.ManagedState.PrepareUnmanagedError); _clusterDao.update(cluster.getId(), cluster); txn.commit(); } } else if( newManagedState.equals(Managed.ManagedState.Managed)) { txn.start(); cluster.setManagedState(Managed.ManagedState.Managed); _clusterDao.update(cluster.getId(), cluster); txn.commit(); } } return cluster; } @Override public Host cancelMaintenance(CancelMaintenanceCmd cmd) { Long hostId = cmd.getId(); // verify input parameters HostVO host = _hostDao.findById(hostId); if (host == null || host.getRemoved() != null) { throw new InvalidParameterValueException("Host with id " + hostId.toString() + " doesn't exist"); } processResourceEvent(ResourceListener.EVENT_CANCEL_MAINTENANCE_BEFORE, hostId); boolean success = cancelMaintenance(hostId); processResourceEvent(ResourceListener.EVENT_CANCEL_MAINTENANCE_AFTER, hostId); if (!success) { throw new CloudRuntimeException("Internal error cancelling maintenance."); } return host; } @Override public Host reconnectHost(ReconnectHostCmd cmd) { Long hostId = cmd.getId(); HostVO host = _hostDao.findById(hostId); if (host == null) { throw new InvalidParameterValueException("Host with id " + hostId.toString() + " doesn't exist"); } return (_agentMgr.reconnect(hostId) ? host : null); } @Override public boolean updateResourceState(Host host, ResourceState.Event event, long msId) throws NoTransitionException { ResourceState currentState = host.getResourceState(); ResourceState nextState = currentState.getNextState(event); if (nextState == null) { throw new NoTransitionException("No next resource state found for current state =" + currentState + " event =" + event); } return _hostDao.updateResourceState(currentState, event, nextState, host); } private boolean doMaintain(final long hostId) { HostVO host = _hostDao.findById(hostId); MaintainAnswer answer = (MaintainAnswer) _agentMgr.easySend(hostId, new MaintainCommand()); if (answer == null || !answer.getResult()) { s_logger.warn("Unable to put host in maintainance mode: " + hostId); return false; } try { updateResourceState(host, ResourceState.Event.AdminAskMaintenace, _nodeId); } catch (NoTransitionException e) { String err = "Cannot transimit resource state of host " + host.getId() + " to " + ResourceState.Maintenance; s_logger.debug(err, e); throw new CloudRuntimeException(err + e.getMessage()); } _agentMgr.pullAgentToMaintenance(hostId); /*TODO: move below to listener */ if (host.getType() == Host.Type.Routing) { final List vms = _vmDao.listByHostId(hostId); if (vms.size() == 0) { return true; } List hosts = _hostDao.listBy(host.getClusterId(), host.getPodId(), host.getDataCenterId()); for (final VMInstanceVO vm : vms) { if (hosts == null || hosts.size() <= 1 || !answer.getMigrate()) { // for the last host in this cluster, stop all the VMs _haMgr.scheduleStop(vm, hostId, WorkType.ForceStop); } else { _haMgr.scheduleMigration(vm); } } } return true; } public boolean maintain(final long hostId) throws AgentUnavailableException { Boolean result = _clusterMgr.propagateResourceEvent(hostId, ResourceState.Event.AdminAskMaintenace); if (result != null) { return result; } return doMaintain(hostId); } @Override public Host maintain(PrepareForMaintenanceCmd cmd) { Long hostId = cmd.getId(); HostVO host = _hostDao.findById(hostId); if (host == null) { s_logger.debug("Unable to find host " + hostId); throw new InvalidParameterValueException("Unable to find host with ID: " + hostId + ". Please specify a valid host ID."); } if (_hostDao.countBy(host.getClusterId(), ResourceState.PrepareForMaintenance, ResourceState.ErrorInMaintenance) > 0) { throw new InvalidParameterValueException("There are other servers in PrepareForMaintenance OR ErrorInMaintenance STATUS in cluster " + host.getClusterId()); } if (_storageMgr.isLocalStorageActiveOnHost(host)) { throw new InvalidParameterValueException("There are active VMs using the host's local storage pool. Please stop all VMs on this host that use local storage."); } try { processResourceEvent(ResourceListener.EVENT_PREPARE_MAINTENANCE_BEFORE, hostId); if (maintain(hostId)) { processResourceEvent(ResourceListener.EVENT_CANCEL_MAINTENANCE_AFTER, hostId); return _hostDao.findById(hostId); } else { throw new CloudRuntimeException("Unable to prepare for maintenance host " + hostId); } } catch (AgentUnavailableException e) { throw new CloudRuntimeException("Unable to prepare for maintenance host " + hostId); } } @Override public Host updateHost(UpdateHostCmd cmd) { Long hostId = cmd.getId(); Long guestOSCategoryId = cmd.getOsCategoryId(); if (guestOSCategoryId != null) { // Verify that the host exists HostVO host = _hostDao.findById(hostId); if (host == null) { throw new InvalidParameterValueException("Host with id " + hostId + " doesn't exist"); } // Verify that the guest OS Category exists if (guestOSCategoryId > 0) { if (_guestOSCategoryDao.findById(guestOSCategoryId) == null) { throw new InvalidParameterValueException("Please specify a valid guest OS category."); } } GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); Map hostDetails = _hostDetailsDao.findDetails(hostId); if (guestOSCategory != null) { // Save a new entry for guest.os.category.id hostDetails.put("guest.os.category.id", String.valueOf(guestOSCategory.getId())); } else { // Delete any existing entry for guest.os.category.id hostDetails.remove("guest.os.category.id"); } _hostDetailsDao.persist(hostId, hostDetails); } String allocationState = cmd.getAllocationState(); if (allocationState != null) { // Verify that the host exists HostVO host = _hostDao.findById(hostId); if (host == null) { throw new InvalidParameterValueException("Host with id " + hostId + " doesn't exist"); } try { HostAllocationState newAllocationState = Host.HostAllocationState.valueOf(allocationState); if (newAllocationState == null) { s_logger.error("Unable to resolve " + allocationState + " to a valid supported allocation State"); throw new InvalidParameterValueException("Unable to resolve " + allocationState + " to a supported state"); } else { host.setHostAllocationState(newAllocationState); } } catch (IllegalArgumentException ex) { s_logger.error("Unable to resolve " + allocationState + " to a valid supported allocation State"); throw new InvalidParameterValueException("Unable to resolve " + allocationState + " to a supported state"); } _hostDao.update(hostId, host); } List hostTags = cmd.getHostTags(); if (hostTags != null) { // Verify that the host exists HostVO host = _hostDao.findById(hostId); if (host == null) { throw new InvalidParameterValueException("Host with id " + hostId + " doesn't exist"); } if(s_logger.isDebugEnabled()){ s_logger.debug("Updating Host Tags to :"+hostTags); } _hostTagsDao.persist(hostId, hostTags); } HostVO updatedHost = _hostDao.findById(hostId); return updatedHost; } @Override public Cluster getCluster(Long clusterId) { return _clusterDao.findById(clusterId); } @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; return true; } @Override public boolean start() { return true; } @Override public boolean stop() { return true; } @Override public String getName() { return _name; } @Override public void registerResourceStateAdapter(String name, ResourceStateAdapter adapter) { if (_resourceStateAdapters.get(name) != null) { throw new CloudRuntimeException(name + " has registered"); } synchronized (_resourceStateAdapters) { _resourceStateAdapters.put(name, adapter); } } @Override public void unregisterResourceStateAdapter(String name) { synchronized (_resourceStateAdapters) { _resourceStateAdapters.remove(name); } } private Object dispatchToStateAdapters(ResourceStateAdapter.Event event, boolean singleTaker, Object... args) { synchronized (_resourceStateAdapters) { Iterator it = _resourceStateAdapters.entrySet().iterator(); Object result = null; while (it.hasNext()) { Map.Entry>> item = (Map.Entry>>) it.next(); ResourceStateAdapter adapter = item.getValue().first(); String msg = new String("Dispatching resource state event " + event + " to " + item.getKey()); s_logger.debug(msg); if (event == ResourceStateAdapter.Event.CREATE_HOST_VO_FOR_CONNECTED) { result = adapter.createHostVOForConnectedAgent((HostVO) args[0], (StartupCommand[]) args[1]); if (result != null && singleTaker) { break; } } else if (event == ResourceStateAdapter.Event.CREATE_HOST_VO_FOR_DIRECT_CONNECT) { result = adapter.createHostVOForDirectConnectAgent((HostVO) args[0], (StartupCommand[]) args[1], (ServerResource) args[2], (Map) args[3], (List) args[4]); if (result != null && singleTaker) { break; } } else if (event == ResourceStateAdapter.Event.DELETE_HOST) { try { result = adapter.deleteHost((HostVO) args[0], (Boolean) args[1], (Boolean) args[2]); if (result != null) { break; } } catch (UnableDeleteHostException e) { s_logger.debug("Adapter " + adapter.getName() + " says unable to delete host", e); result = new ResourceStateAdapter.DeleteHostAnswer(false, true); } } else { throw new CloudRuntimeException("Unknown resource state event:" + event); } } return result; } } @Override public void checkCIDR(HostPodVO pod, DataCenterVO dc, String serverPrivateIP, String serverPrivateNetmask) throws IllegalArgumentException { if (serverPrivateIP == null) { return; } // Get the CIDR address and CIDR size String cidrAddress = pod.getCidrAddress(); long cidrSize = pod.getCidrSize(); // If the server's private IP address is not in the same subnet as the // pod's CIDR, return false String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSize); String serverSubnet = NetUtils.getSubNet(serverPrivateIP, serverPrivateNetmask); if (!cidrSubnet.equals(serverSubnet)) { s_logger.warn("The private ip address of the server (" + serverPrivateIP + ") is not compatible with the CIDR of pod: " + pod.getName() + " and zone: " + dc.getName()); throw new IllegalArgumentException("The private ip address of the server (" + serverPrivateIP + ") is not compatible with the CIDR of pod: " + pod.getName() + " and zone: " + dc.getName()); } // If the server's private netmask is less inclusive than the pod's CIDR // netmask, return false String cidrNetmask = NetUtils.getCidrSubNet("255.255.255.255", cidrSize); long cidrNetmaskNumeric = NetUtils.ip2Long(cidrNetmask); long serverNetmaskNumeric = NetUtils.ip2Long(serverPrivateNetmask); if (serverNetmaskNumeric > cidrNetmaskNumeric) { throw new IllegalArgumentException("The private ip address of the server (" + serverPrivateIP + ") is not compatible with the CIDR of pod: " + pod.getName() + " and zone: " + dc.getName()); } } private boolean checkCIDR(HostPodVO pod, String serverPrivateIP, String serverPrivateNetmask) { if (serverPrivateIP == null) { return true; } // Get the CIDR address and CIDR size String cidrAddress = pod.getCidrAddress(); long cidrSize = pod.getCidrSize(); // If the server's private IP address is not in the same subnet as the // pod's CIDR, return false String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSize); String serverSubnet = NetUtils.getSubNet(serverPrivateIP, serverPrivateNetmask); if (!cidrSubnet.equals(serverSubnet)) { return false; } // If the server's private netmask is less inclusive than the pod's CIDR // netmask, return false String cidrNetmask = NetUtils.getCidrSubNet("255.255.255.255", cidrSize); long cidrNetmaskNumeric = NetUtils.ip2Long(cidrNetmask); long serverNetmaskNumeric = NetUtils.ip2Long(serverPrivateNetmask); if (serverNetmaskNumeric > cidrNetmaskNumeric) { return false; } return true; } protected HostVO createHostVO(StartupCommand[] cmds, ServerResource resource, Map details, List hostTags, ResourceStateAdapter.Event stateEvent) { StartupCommand startup = cmds[0]; HostVO host = _hostDao.findByGuid(startup.getGuid()); boolean isNew = false; if (host == null) { host = _hostDao.findByGuid(startup.getGuidWithoutResource()); } if (host == null) { host = new HostVO(startup.getGuid()); isNew = true; } // TODO: we don't allow to set ResourceState here? String dataCenter = startup.getDataCenter(); String pod = startup.getPod(); String cluster = startup.getCluster(); if (pod != null && dataCenter != null && pod.equalsIgnoreCase("default") && dataCenter.equalsIgnoreCase("default")) { List pods = _podDao.listAllIncludingRemoved(); for (HostPodVO hpv : pods) { if (checkCIDR(hpv, startup.getPrivateIpAddress(), startup.getPrivateNetmask())) { pod = hpv.getName(); dataCenter = _dcDao.findById(hpv.getDataCenterId()).getName(); break; } } } long dcId = -1; DataCenterVO dc = _dcDao.findByName(dataCenter); if (dc == null) { try { dcId = Long.parseLong(dataCenter); dc = _dcDao.findById(dcId); } catch (final NumberFormatException e) { } } if (dc == null) { throw new IllegalArgumentException("Host " + startup.getPrivateIpAddress() + " sent incorrect data center: " + dataCenter); } dcId = dc.getId(); HostPodVO p = _podDao.findByName(pod, dcId); if (p == null) { try { final long podId = Long.parseLong(pod); p = _podDao.findById(podId); } catch (final NumberFormatException e) { } } /* * ResourceStateAdapter is responsible for throwing Exception if Pod is * null and non-null is required. for example, XcpServerDiscoever. * Others, like PxeServer, ExternalFireware don't require Pod */ Long podId = (p == null ? null : p.getId()); Long clusterId = null; if (cluster != null) { try { clusterId = Long.valueOf(cluster); } catch (NumberFormatException e) { ClusterVO c = _clusterDao.findBy(cluster, podId); if (c == null) { c = new ClusterVO(dcId, podId, cluster); c = _clusterDao.persist(c); } clusterId = c.getId(); } } host.setDataCenterId(dc.getId()); host.setPodId(podId); host.setClusterId(clusterId); host.setPrivateIpAddress(startup.getPrivateIpAddress()); host.setPrivateNetmask(startup.getPrivateNetmask()); host.setPrivateMacAddress(startup.getPrivateMacAddress()); host.setPublicIpAddress(startup.getPublicIpAddress()); host.setPublicMacAddress(startup.getPublicMacAddress()); host.setPublicNetmask(startup.getPublicNetmask()); host.setStorageIpAddress(startup.getStorageIpAddress()); host.setStorageMacAddress(startup.getStorageMacAddress()); host.setStorageNetmask(startup.getStorageNetmask()); host.setVersion(startup.getVersion()); host.setName(startup.getName()); host.setManagementServerId(_nodeId); host.setStorageUrl(startup.getIqn()); host.setLastPinged(System.currentTimeMillis() >> 10); host.setHostTags(hostTags); host.setDetails(details); if (startup.getStorageIpAddressDeux() != null) { host.setStorageIpAddressDeux(startup.getStorageIpAddressDeux()); host.setStorageMacAddressDeux(startup.getStorageMacAddressDeux()); host.setStorageNetmaskDeux(startup.getStorageNetmaskDeux()); } if (resource != null) { /* null when agent is connected agent */ host.setResource(resource.getClass().getName()); } host = (HostVO) dispatchToStateAdapters(stateEvent, true, host, cmds, resource, details, hostTags); assert host != null : "No resource state adapter response"; if (isNew) { _hostDao.persist(host); } else { _hostDao.update(host.getId(), host); } /* Agent goes to Connecting status */ _agentMgr.agentStatusTransitTo(host, Status.Event.AgentConnected, _nodeId); try { updateResourceState(host, ResourceState.Event.InternalCreated, _nodeId); } catch (NoTransitionException e) { s_logger.debug("Cannot transmit host " + host.getId() + "to Enabled state", e); } return host; } private Host createHostAndAgent(ServerResource resource, Map details, boolean old, List hostTags, String allocationState, boolean forRebalance) { HostVO host = null; AgentAttache attache = null; StartupCommand[] cmds = null; try { cmds = resource.initialize(); if (cmds == null) { s_logger.info("Unable to fully initialize the agent because no StartupCommands are returned"); return null; } if (s_logger.isDebugEnabled()) { new Request(-1l, -1l, cmds, true, false).logD("Startup request from directly connected host: ", true); } if (old) { StartupCommand firstCmd = cmds[0]; host = _hostDao.findByGuid(firstCmd.getGuid()); if (host == null) { host = _hostDao.findByGuid(firstCmd.getGuidWithoutResource()); } if (host != null && host.getRemoved() == null) { s_logger.debug("Found the host " + host.getId() + " by guid: " + firstCmd.getGuid() + ", old host reconnected as new"); return null; } } host = createHostVO(cmds, resource, details, hostTags, ResourceStateAdapter.Event.CREATE_HOST_VO_FOR_DIRECT_CONNECT); if (host != null) { attache = _agentMgr.handleDirectConnectAgent(host, cmds, resource, forRebalance); /* reload myself from database */ host = _hostDao.findById(host.getId()); } } catch (Exception e) { s_logger.warn("Unable to connect due to ", e); } finally { if (attache == null) { if (cmds != null) { resource.disconnected(); } if (host != null) { /* Change agent status to Alert */ _agentMgr.agentStatusTransitTo(host, Status.Event.AgentDisconnected, _nodeId); try { updateResourceState(host, ResourceState.Event.Disable, _nodeId); } catch (NoTransitionException e) { s_logger.debug("Cannot transmit host " + host.getId() + "to Disabled state", e); } } } else { try { updateResourceState(host, ResourceState.Event.InternalCreated, _nodeId); } catch (NoTransitionException e) { s_logger.debug("Cannot transmit host " + host.getId() + "to Enabled state", e); } } } return host; } @Override public Host createHostAndAgent(Long hostId, ServerResource resource, Map details, boolean old, List hostTags, String allocationState, boolean forRebalance) { _agentMgr.tapLoadingAgents(hostId, TapAgentsAction.Add); Host host = createHostAndAgent(resource, details, old, hostTags, allocationState, forRebalance); _agentMgr.tapLoadingAgents(hostId, TapAgentsAction.Del); return host; } @Override public Host addHost(long zoneId, ServerResource resource, Type hostType, Map hostDetails) { // Check if the zone exists in the system if (_dcDao.findById(zoneId) == null) { throw new InvalidParameterValueException("Can't find zone with id " + zoneId); } Map details = hostDetails; String guid = details.get("guid"); List currentHosts = _hostDao.listBy(hostType, zoneId); for (HostVO currentHost : currentHosts) { if (currentHost.getGuid().equals(guid)) { return currentHost; } } return createHostAndAgent(resource, hostDetails, true, null, null, false); } @Override public HostVO createHostVOForConnectedAgent(StartupCommand[] cmds) { return createHostVO(cmds, null, null, null, ResourceStateAdapter.Event.CREATE_HOST_VO_FOR_CONNECTED); } private void checkIPConflicts(HostPodVO pod, DataCenterVO dc, String serverPrivateIP, String serverPrivateNetmask, String serverPublicIP, String serverPublicNetmask) { // If the server's private IP is the same as is public IP, this host has // a host-only private network. Don't check for conflicts with the // private IP address table. if (serverPrivateIP != serverPublicIP) { if (!_privateIPAddressDao.mark(dc.getId(), pod.getId(), serverPrivateIP)) { // If the server's private IP address is already in the // database, return false List existingPrivateIPs = _privateIPAddressDao.listByPodIdDcIdIpAddress(pod.getId(), dc.getId(), serverPrivateIP); assert existingPrivateIPs.size() <= 1 : " How can we get more than one ip address with " + serverPrivateIP; if (existingPrivateIPs.size() > 1) { throw new IllegalArgumentException("The private ip address of the server (" + serverPrivateIP + ") is already in use in pod: " + pod.getName() + " and zone: " + dc.getName()); } if (existingPrivateIPs.size() == 1) { DataCenterIpAddressVO vo = existingPrivateIPs.get(0); if (vo.getInstanceId() != null) { throw new IllegalArgumentException("The private ip address of the server (" + serverPrivateIP + ") is already in use in pod: " + pod.getName() + " and zone: " + dc.getName()); } } } } if (serverPublicIP != null && !_publicIPAddressDao.mark(dc.getId(), new Ip(serverPublicIP))) { // If the server's public IP address is already in the database, // return false List existingPublicIPs = _publicIPAddressDao.listByDcIdIpAddress(dc.getId(), serverPublicIP); if (existingPublicIPs.size() > 0) { throw new IllegalArgumentException("The public ip address of the server (" + serverPublicIP + ") is already in use in zone: " + dc.getName()); } } } @Override public HostVO fillRoutingHostVO(HostVO host, StartupRoutingCommand ssCmd, HypervisorType hyType, Map details, List hostTags) { if (host.getPodId() == null) { s_logger.error("Host " + ssCmd.getPrivateIpAddress() + " sent incorrect pod, pod id is null"); throw new IllegalArgumentException("Host " + ssCmd.getPrivateIpAddress() + " sent incorrect pod, pod id is null"); } ClusterVO clusterVO = _clusterDao.findById(host.getClusterId()); if (clusterVO.getHypervisorType() != hyType) { throw new IllegalArgumentException("Can't add host whose hypervisor type is: " + hyType + " into cluster: " + clusterVO.getId() + " whose hypervisor type is: " + clusterVO.getHypervisorType()); } final Map hostDetails = ssCmd.getHostDetails(); if (hostDetails != null) { if (details != null) { details.putAll(hostDetails); } else { details = hostDetails; } } HostPodVO pod = _podDao.findById(host.getPodId()); DataCenterVO dc = _dcDao.findById(host.getDataCenterId()); checkIPConflicts(pod, dc, ssCmd.getPrivateIpAddress(), ssCmd.getPublicIpAddress(), ssCmd.getPublicIpAddress(), ssCmd.getPublicNetmask()); host.setType(com.cloud.host.Host.Type.Routing); host.setDetails(details); host.setCaps(ssCmd.getCapabilities()); host.setCpus(ssCmd.getCpus()); host.setTotalMemory(ssCmd.getMemory()); host.setSpeed(ssCmd.getSpeed()); host.setHypervisorType(hyType); return host; } @Override public void deleteRoutingHost(HostVO host, boolean isForced, boolean forceDestroyStorage) throws UnableDeleteHostException { if (host.getType() != Host.Type.Routing) { throw new CloudRuntimeException("Non-Routing host gets in deleteRoutingHost, id is " + host.getId()); } if (s_logger.isDebugEnabled()) { s_logger.debug("Deleting Host: " + host.getId() + " Guid:" + host.getGuid()); } User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); if (forceDestroyStorage) { // put local storage into mainenance mode, will set all the VMs on // this local storage into stopped state StoragePool storagePool = _storageMgr.findLocalStorageOnHost(host.getId()); if (storagePool != null) { if (storagePool.getStatus() == StoragePoolStatus.Up || storagePool.getStatus() == StoragePoolStatus.ErrorInMaintenance) { try { storagePool = _storageSvr.preparePrimaryStorageForMaintenance(storagePool.getId()); if (storagePool == null) { s_logger.debug("Failed to set primary storage into maintenance mode"); throw new UnableDeleteHostException("Failed to set primary storage into maintenance mode"); } } catch (Exception e) { s_logger.debug("Failed to set primary storage into maintenance mode, due to: " + e.toString()); throw new UnableDeleteHostException("Failed to set primary storage into maintenance mode, due to: " + e.toString()); } } List vmsOnLocalStorage = _storageMgr.listByStoragePool(storagePool.getId()); for (VMInstanceVO vm : vmsOnLocalStorage) { try { if (!_vmMgr.destroy(vm, caller, _accountMgr.getAccount(vm.getAccountId()))) { String errorMsg = "There was an error Destory the vm: " + vm + " as a part of hostDelete id=" + host.getId(); s_logger.warn(errorMsg); throw new UnableDeleteHostException(errorMsg); } } catch (Exception e) { String errorMsg = "There was an error Destory the vm: " + vm + " as a part of hostDelete id=" + host.getId(); s_logger.debug(errorMsg, e); throw new UnableDeleteHostException(errorMsg + "," + e.getMessage()); } } } } else { // Check if there are vms running/starting/stopping on this host List vms = _vmDao.listByHostId(host.getId()); if (!vms.isEmpty()) { if (isForced) { // Stop HA disabled vms and HA enabled vms in Stopping state // Restart HA enabled vms for (VMInstanceVO vm : vms) { if (!vm.isHaEnabled() || vm.getState() == State.Stopping) { s_logger.debug("Stopping vm: " + vm + " as a part of deleteHost id=" + host.getId()); try { if (!_vmMgr.advanceStop(vm, true, caller, _accountMgr.getAccount(vm.getAccountId()))) { String errorMsg = "There was an error stopping the vm: " + vm + " as a part of hostDelete id=" + host.getId(); s_logger.warn(errorMsg); throw new UnableDeleteHostException(errorMsg); } } catch (Exception e) { String errorMsg = "There was an error stopping the vm: " + vm + " as a part of hostDelete id=" + host.getId(); s_logger.debug(errorMsg, e); throw new UnableDeleteHostException(errorMsg + "," + e.getMessage()); } } else if (vm.isHaEnabled() && (vm.getState() == State.Running || vm.getState() == State.Starting)) { s_logger.debug("Scheduling restart for vm: " + vm + " " + vm.getState() + " on the host id=" + host.getId()); _haMgr.scheduleRestart(vm, false); } } } else { throw new UnableDeleteHostException("Unable to delete the host as there are vms in " + vms.get(0).getState() + " state using this host and isForced=false specified"); } } } } private boolean doCancelMaintenance(long hostId) { HostVO host; host = _hostDao.findById(hostId); if (host == null || host.getRemoved() != null) { s_logger.warn("Unable to find host " + hostId); return true; } /*TODO: think twice about returning true or throwing out exception, I really prefer to exception that always exposes bugs */ if (host.getResourceState() != ResourceState.PrepareForMaintenance && host.getResourceState() != ResourceState.Maintenance && host.getResourceState() != ResourceState.ErrorInMaintenance) { throw new CloudRuntimeException("Cannot perform cancelMaintenance when resource state is " + host.getResourceState() + ", hostId = " + hostId); } /*TODO: move to listener */ _haMgr.cancelScheduledMigrations(host); List vms = _haMgr.findTakenMigrationWork(); for (VMInstanceVO vm : vms) { if (vm.getHostId() != null && vm.getHostId() == hostId) { s_logger.info("Unable to cancel migration because the vm is being migrated: " + vm); return false; } } try { updateResourceState(host, ResourceState.Event.AdminCancelMaintenance, _nodeId); _agentMgr.disconnectWithoutInvestigation(hostId, Status.Event.ResetRequested); return true; } catch (NoTransitionException e) { s_logger.debug("Cannot transmit host " + host.getId() + "to Enabled state", e); return false; } } private boolean cancelMaintenance(long hostId) { try { Boolean result = _clusterMgr.propagateResourceEvent(hostId, ResourceState.Event.AdminCancelMaintenance); if (result != null) { return result; } } catch (AgentUnavailableException e) { return false; } return doCancelMaintenance(hostId); } @Override public boolean executeUserRequest(long hostId, ResourceState.Event event) throws AgentUnavailableException { if (event == ResourceState.Event.AdminAskMaintenace) { return doMaintain(hostId); } else if (event == ResourceState.Event.AdminCancelMaintenance) { return doCancelMaintenance(hostId); } else if (event == ResourceState.Event.DeleteHost) { /*TODO: Ask alex why we assume the last two parameters are false*/ return doDeleteHost(hostId, false, false); } else if (event == ResourceState.Event.Unmanaged) { return doUmanageHost(hostId); } else if (event == ResourceState.Event.UpdatePassword) { return doUpdateHostPassword(hostId); } else { throw new CloudRuntimeException("Received an resource event we are not handling now, " + event); } } private boolean doUmanageHost(long hostId) { try { HostVO host = _hostDao.findById(hostId); if (host == null) { s_logger.debug("Cannot find host " + hostId + ", assuming it has been deleted, skip umanage"); return true; } updateResourceState(host, ResourceState.Event.AdminCancelMaintenance, _nodeId); _agentMgr.disconnectWithoutInvestigation(hostId, Status.Event.AgentDisconnected); return true; } catch (NoTransitionException e) { s_logger.debug("Cannot transmit host " + hostId + "to Enabled state", e); return false; } } @Override public boolean umanageHost(long hostId) { try { Boolean result = _clusterMgr.propagateResourceEvent(hostId, ResourceState.Event.Unmanaged); if (result != null) { return result; } } catch (AgentUnavailableException e) { return false; } return doUmanageHost(hostId); } private boolean doUpdateHostPassword(long hostId) { AgentAttache attache = _agentMgr.findAttache(hostId); if (attache == null) { return false; } DetailVO nv = _detailsDao.findDetail(hostId, ApiConstants.USERNAME); String username = nv.getValue(); nv = _detailsDao.findDetail(hostId, ApiConstants.PASSWORD); String password = nv.getValue(); UpdateHostPasswordCommand cmd = new UpdateHostPasswordCommand(username, password); attache.updatePassword(cmd); return true; } @Override public boolean updateHostPassword(UpdateHostPasswordCmd cmd) { if (cmd.getClusterId() == null) { // update agent attache password try { Boolean result = _clusterMgr.propagateResourceEvent(cmd.getHostId(), ResourceState.Event.UpdatePassword); if (result != null) { return result; } } catch (AgentUnavailableException e) { } return doUpdateHostPassword(cmd.getHostId()); } else { // get agents for the cluster List hosts = _hostDao.listByCluster(cmd.getClusterId()); for (HostVO h : hosts) { try { /*FIXME: this is a buggy logic, check with alex. Shouldn't return if propagation return non null*/ Boolean result = _clusterMgr.propagateResourceEvent(h.getId(), ResourceState.Event.UpdatePassword); if (result != null) { return result; } doUpdateHostPassword(cmd.getHostId()); } catch (AgentUnavailableException e) { } } return true; } } @Override public boolean maintenanceFailed(long hostId) { HostVO host = _hostDao.findById(hostId); if (host == null) { if (s_logger.isDebugEnabled()) { s_logger.debug("Cant not find host " + hostId); } return false; } else { try { return updateResourceState(host, ResourceState.Event.UnableToMigrate, _nodeId); } catch (NoTransitionException e) { s_logger.debug("No next resource state for host " + host.getId() + " while current state is " + host.getResourceState() + " with event " + ResourceState.Event.UnableToMigrate, e); return false; } } } }