// Copyright 2012 Citrix Systems, Inc. Licensed under the
// Apache License, Version 2.0 (the "License"); you may not use this
// file except in compliance with the License. Citrix Systems, Inc.
// reserves all rights not expressly granted by 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.
//
// Automatically generated by addcopyright.py at 04/03/2012
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 java.util.Set;
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.Answer;
import com.cloud.agent.api.GetHostStatsAnswer;
import com.cloud.agent.api.GetHostStatsCommand;
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.UnsupportedAnswer;
import com.cloud.agent.api.UpdateHostPasswordCommand;
import com.cloud.agent.manager.AgentAttache;
import com.cloud.agent.manager.allocator.PodAllocator;
import com.cloud.agent.transport.Request;
import com.cloud.api.ApiConstants;
import com.cloud.api.ApiDBUtils;
import com.cloud.api.commands.AddClusterCmd;
import com.cloud.api.commands.AddHostCmd;
import com.cloud.api.commands.AddSecondaryStorageCmd;
import com.cloud.api.commands.AddSwiftCmd;
import com.cloud.api.commands.CancelMaintenanceCmd;
import com.cloud.api.commands.DeleteClusterCmd;
import com.cloud.api.commands.ListSwiftsCmd;
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.configuration.Config;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.ClusterVSMMapVO;
import com.cloud.dc.DataCenterIpAddressVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.PodCluster;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.ClusterVSMMapDao;
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.exception.ResourceInUseException;
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.Type;
import com.cloud.host.HostStats;
import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.host.Status.Event;
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.CiscoNexusVSMDeviceVO;
import com.cloud.network.IPAddressVO;
import com.cloud.network.dao.CiscoNexusVSMDeviceDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.org.Cluster;
import com.cloud.org.Grouping;
import com.cloud.org.Grouping.AllocationState;
import com.cloud.org.Managed;
import com.cloud.service.ServiceOfferingVO;
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.Swift;
import com.cloud.storage.SwiftVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.StoragePoolDao;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.secondary.SecondaryStorageVmManager;
import com.cloud.storage.swift.SwiftManager;
import com.cloud.template.VirtualMachineTemplate;
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.StringUtils;
import com.cloud.utils.UriUtils;
import com.cloud.utils.cisco.n1kv.vsm.NetconfHelper;
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.SearchCriteria.Op;
import com.cloud.utils.db.SearchCriteria2;
import com.cloud.utils.db.SearchCriteriaService;
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.utils.ssh.SSHCmdHelper;
import com.cloud.utils.ssh.sshException;
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 ClusterVSMMapDao _clusterVSMDao;
@Inject
protected CiscoNexusVSMDeviceDao _vsmDao;
@Inject
protected CapacityDao _capacityDao;
@Inject
protected HostDao _hostDao;
@Inject
protected SwiftManager _swiftMgr;
@Inject
protected HostDetailsDao _hostDetailsDao;
@Inject
protected ConfigurationDao _configDao;
@Inject
protected HostTagsDao _hostTagsDao;
@Inject
protected GuestOSCategoryDao _guestOSCategoryDao;
@Inject
protected StoragePoolDao _storagePoolDao;
@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<? extends Discoverer> _discoverers;
@Inject
protected ClusterManager _clusterMgr;
@Inject
protected StoragePoolHostDao _storagePoolHostDao;
@Inject(adapter = PodAllocator.class)
protected Adapters<PodAllocator> _podAllocators = null;
@Inject
protected VMTemplateDao _templateDao;
@Inject
protected ConfigurationManager _configMgr;
@Inject
protected ClusterVSMMapDao _clusterVSMMapDao;
protected long _nodeId = ManagementServerNode.getManagementServerId();
protected HashMap<String, ResourceStateAdapter> _resourceStateAdapters = new HashMap<String, ResourceStateAdapter>();
protected HashMap<Integer, List<ResourceListener>> _lifeCycleListeners = new HashMap<Integer, List<ResourceListener>>();
private HypervisorType _defaultSystemVMHypervisor;
private void insertListener(Integer event, ResourceListener listener) {
List<ResourceListener> lst = _lifeCycleListeners.get(event);
if (lst == null) {
lst = new ArrayList<ResourceListener>();
_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<Integer, List<ResourceListener>> items = (Map.Entry<Integer, List<ResourceListener>>)it.next();
List<ResourceListener> lst = items.getValue();
lst.remove(listener);
}
}
}
protected void processResourceEvent(Integer event, Object...params) {
List<ResourceListener> 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<String>) params[6]);
eventName = "EVENT_DISCOVER_BEFORE";
} else if (event == ResourceListener.EVENT_DISCOVER_AFTER) {
l.processDiscoverEventAfter((Map<? extends ServerResource, Map<String, String>>) 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());
}
}
@DB
@Override
public List<? extends Cluster> discoverCluster(AddClusterCmd cmd) throws IllegalArgumentException, DiscoveryException, ResourceInUseException {
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) {
InvalidParameterValueException ex = new InvalidParameterValueException("Can't find zone by the id specified");
ex.addProxyObject(zone, dcId, "dcId");
throw ex;
}
Account account = UserContext.current().getCaller();
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(account.getType())) {
PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation, Zone with specified id is currently disabled");
ex.addProxyObject(zone, dcId, "dcId");
throw ex;
}
HostPodVO pod = _podDao.findById(podId);
if (pod == null) {
throw new InvalidParameterValueException("Can't find pod with specified podId " + podId);
}
// Check if the pod exists in the system
if (_podDao.findById(podId) == null) {
throw new InvalidParameterValueException("Can't find pod by id " + podId);
}
// check if pod belongs to the zone
if (!Long.valueOf(pod.getDataCenterId()).equals(dcId)) {
InvalidParameterValueException ex = new InvalidParameterValueException("Pod with specified id doesn't belong to the zone " + dcId);
ex.addProxyObject(pod, podId, "podId");
ex.addProxyObject(zone, dcId, "dcId");
throw ex;
}
// 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<ClusterVO> result = new ArrayList<ClusterVO>();
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
CloudRuntimeException ex = new CloudRuntimeException("Unable to create cluster " + clusterName + " in pod and data center with specified ids", e);
// Get the pod VO object's table name.
ex.addProxyObject(pod, podId, "podId");
ex.addProxyObject(zone, dcId, "dcId");
throw ex;
}
clusterId = cluster.getId();
result.add(cluster);
// Check if we're associating a Cisco Nexus VSM with a vmware cluster.
if (hypervisorType == HypervisorType.VMware &&
Boolean.parseBoolean(_configDao.getValue(Config.VmwareUseNexusVSwitch.toString()))) {
String vsmIp = cmd.getVSMIpaddress();
String vsmUser = cmd.getVSMUsername();
String vsmPassword = cmd.getVSMPassword();
if(vsmIp != null && vsmUser != null && vsmPassword != null) {
NetconfHelper netconfClient;
try {
netconfClient = new NetconfHelper(vsmIp, vsmUser, vsmPassword);
netconfClient.disconnect();
} catch (CloudRuntimeException e) {
String msg = "Invalid credentials supplied for user " + vsmUser + " for Cisco Nexus 1000v VSM at " + vsmIp;
s_logger.error(msg);
_clusterDao.remove(clusterId);
throw new CloudRuntimeException(msg);
}
Transaction txn;
// If VSM already exists and is mapped to a cluster, fail this operation.
CiscoNexusVSMDeviceVO vsm = _vsmDao.getVSMbyIpaddress(vsmIp);
if(vsm != null) {
List<ClusterVSMMapVO> clusterList = _clusterVSMDao.listByVSMId(vsm.getId());
if (clusterList != null && !clusterList.isEmpty()) {
s_logger.error("Failed to add cluster: specified Nexus VSM is already associated with another cluster");
_clusterDao.remove(clusterId);
ResourceInUseException ex = new ResourceInUseException("Failed to add cluster: specified Nexus VSM is already associated with another cluster with specified Id");
ex.addProxyObject("cluster", clusterList.get(0).getClusterId(), "clusterId");
throw ex;
}
}
// persist credentials to database if the VSM entry is not already in the db.
if (_vsmDao.getVSMbyIpaddress(vsmIp) == null) {
vsm = new CiscoNexusVSMDeviceVO(vsmIp, vsmUser, vsmPassword);
txn = Transaction.currentTxn();
try {
txn.start();
vsm = _vsmDao.persist(vsm);
txn.commit();
} catch (Exception e) {
txn.rollback();
s_logger.error("Failed to persist Cisco Nexus 1000v VSM details to database. Exception: " + e.getMessage());
// Removing the cluster record which was added already because the persistence of Nexus VSM credentials has failed.
_clusterDao.remove(clusterId);
throw new CloudRuntimeException(e.getMessage());
}
}
// Create a mapping between the cluster and the vsm.
vsm = _vsmDao.getVSMbyIpaddress(vsmIp);
if (vsm != null) {
ClusterVSMMapVO connectorObj = new ClusterVSMMapVO(clusterId, vsm.getId());
txn = Transaction.currentTxn();
try {
txn.start();
_clusterVSMDao.persist(connectorObj);
txn.commit();
} catch (Exception e) {
txn.rollback();
s_logger.error("Failed to associate Cisco Nexus 1000v VSM with cluster: " + clusterName + ". Exception: " + e.getMessage());
_clusterDao.remove(clusterId);
throw new CloudRuntimeException(e.getMessage());
}
}
} else {
String msg;
msg = "The global parameter " + Config.VmwareUseNexusVSwitch.toString() +
" is set to \"true\". Following mandatory parameters are not specified. ";
if(vsmIp == null) {
msg += "vsmipaddress: Management IP address of Cisco Nexus 1000v dvSwitch. ";
}
if(vsmUser == null) {
msg += "vsmusername: Name of a user account with admin privileges over Cisco Nexus 1000v dvSwitch. ";
}
if(vsmPassword == null) {
if(vsmUser != null) {
msg += "vsmpassword: Password of user account " + vsmUser + ". ";
} else {
msg += "vsmpassword: Password of user account with admin privileges over Cisco Nexus 1000v dvSwitch. ";
}
}
s_logger.error(msg);
// Cleaning up the cluster record as addCluster operation failed because Nexus dvSwitch credentials are supplied.
_clusterDao.remove(clusterId);
throw new CloudRuntimeException(msg);
}
}
if (clusterType == Cluster.ClusterType.CloudManaged) {
return result;
}
// save cluster details for later cluster/host cross-checking
Map<String, String> details = new HashMap<String, String>();
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<HostVO> hosts = new ArrayList<HostVO>();
Map<? extends ServerResource, Map<String, String>> resources = null;
resources = discoverer.find(dcId, podId, clusterId, uri, username, password, null);
if (resources != null) {
for (Map.Entry<? extends ServerResource, Map<String, String>> 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, 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);
_clusterVSMDao.removeByClusterId(clusterId);
}
}
}
@Override
public Discoverer getMatchingDiscover(Hypervisor.HypervisorType hypervisorType) {
Enumeration<? extends Discoverer> en = _discoverers.enumeration();
while (en.hasMoreElements()) {
Discoverer discoverer = en.nextElement();
if (discoverer.getHypervisorType() == hypervisorType) {
return discoverer;
}
}
return null;
}
@Override
public List<? extends Host> 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<String> 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) {
InvalidParameterValueException ex = new InvalidParameterValueException("can not find cluster for specified clusterId");
ex.addProxyObject(cluster, clusterId, "clusterId");
throw ex;
} else {
if (cluster.getGuid() == null) {
List<HostVO> hosts = listAllHostsInCluster(clusterId);
if (!hosts.isEmpty()) {
CloudRuntimeException ex = new CloudRuntimeException("Guid is not updated for cluster with specified cluster id; need to wait for hosts in this cluster to come up");
ex.addProxyObject(cluster, clusterId, "clusterId");
throw ex;
}
}
}
}
return discoverHostsFull(dcId, podId, clusterId, clusterName, url, username, password, cmd.getHypervisor(), hostTags, cmd.getFullUrlParams());
}
@Override
public List<? extends Host> 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);
}
@Override
public Swift discoverSwift(AddSwiftCmd cmd) throws DiscoveryException {
return _swiftMgr.addSwift(cmd);
}
@Override
public List<SwiftVO> listSwifts(ListSwiftsCmd cmd) {
return _swiftMgr.listSwifts(cmd);
}
private List<HostVO> discoverHostsFull(Long dcId, Long podId, Long clusterId, String clusterName, String url, String username, String password, String hypervisorType, List<String> hostTags,
Map<String, String> params) 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())) {
PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation, Zone with specified id is currently disabled");
ex.addProxyObject(zone, dcId, "dcId");
throw ex;
}
// Check if the pod exists in the system
if (podId != null) {
HostPodVO pod = _podDao.findById(podId);
if (pod == null) {
throw new InvalidParameterValueException("Can't find pod by id " + podId);
}
// check if pod belongs to the zone
if (!Long.valueOf(pod.getDataCenterId()).equals(dcId)) {
InvalidParameterValueException ex = new InvalidParameterValueException("Pod with specified podId" + podId + " doesn't belong to the zone with specified zoneId" + dcId);
ex.addProxyObject(pod, podId, "podId");
ex.addProxyObject(zone, dcId, "dcId");
throw ex;
}
}
// 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<String, String> 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) {
HostPodVO pod = _podDao.findById(podId);
if (pod == null) {
throw new InvalidParameterValueException("Can't find pod by id " + podId);
}
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) {
CloudRuntimeException ex = new CloudRuntimeException("Unable to create cluster " + clusterName + " in pod with specified podId and data center with specified dcID", e);
ex.addProxyObject(pod, podId, "podId");
ex.addProxyObject(zone, dcId, "dcId");
throw ex;
}
}
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<HostVO> hosts = new ArrayList<HostVO>();
s_logger.info("Trying to add a new host at " + url + " in data center " + dcId);
Enumeration<? extends Discoverer> 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<? extends ServerResource, Map<String, String>> 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<? extends ServerResource, Map<String, String>> 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<String, String> details = entry.getValue();
String guid = details.get("guid");
List<HostVO> kvmHosts = listAllUpAndEnabledHosts(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, 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
protected 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<StoragePoolHostVO> pools = _storagePoolHostDao.listByHostIdIncludingRemoved(hostId);
ResourceStateAdapter.DeleteHostAnswer answer = (ResourceStateAdapter.DeleteHostAnswer) dispatchToStateAdapters(ResourceStateAdapter.Event.DELETE_HOST, false, host, new Boolean(isForced), new Boolean(isForceDeleteStorage));
if (answer == null) {
throw new CloudRuntimeException("No resource adapter respond to DELETE_HOST event for " + host.getName() + " id = " + hostId + ", hypervisorType is " + host.getHypervisorType() + ", host type is " + host.getType());
}
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<HostVO> hosts = listAllHostsInCluster(clusterId);
if (hosts.size() == 0) {
ClusterVO cluster = _clusterDao.findById(clusterId);
cluster.setGuid(null);
_clusterDao.update(clusterId, cluster);
}
}
try {
resourceStateTransitTo(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<CapacityVO> 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();
throw new CloudRuntimeException("Cluster: " + cmd.getId() + " does not exist");
}
Hypervisor.HypervisorType hypervisorType = cluster.getHypervisorType();
List<HostVO> hosts = listAllHostsInCluster(cmd.getId());
if (hosts.size() > 0) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Cluster: " + cmd.getId() + " still has hosts, can't remove");
}
txn.rollback();
throw new CloudRuntimeException("Cluster: " + cmd.getId() + " cannot be removed. Cluster still has hosts");
}
//don't allow to remove the cluster if it has non-removed storage pools
List<StoragePoolVO> storagePools = _storagePoolDao.listPoolsByCluster(cmd.getId());
if (storagePools.size() > 0) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Cluster: " + cmd.getId() + " still has storage pools, can't remove");
}
txn.rollback();
throw new CloudRuntimeException("Cluster: " + cmd.getId() + " cannot be removed. Cluster still has storage pools");
}
if (_clusterDao.remove(cmd.getId())){
_capacityDao.removeBy(null, null, null, cluster.getId(), null);
// If this cluster is of type vmware, and if the nexus vswitch global parameter setting is turned
// on, remove the row in cluster_vsm_map for this cluster id.
if (hypervisorType == HypervisorType.VMware &&
Boolean.parseBoolean(_configDao.getValue(Config.VmwareUseNexusVSwitch.toString()))) {
_clusterVSMMapDao.removeByClusterId(cmd.getId());
}
}
txn.commit();
return true;
} catch(CloudRuntimeException e){
throw e;
} 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 {
_capacityDao.updateCapacityState(null, null, cluster.getId(), null, allocationState);
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<HostVO> hosts = listAllUpAndEnabledHosts(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 = listAllUpAndEnabledHosts(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 resourceStateTransitTo(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);
}
// TO DO - Make it more granular and have better conversion into capacity type
if(host.getType() == Type.Routing && host.getClusterId() != null){
AllocationState capacityState = _configMgr.findClusterAllocationState(ApiDBUtils.findClusterById(host.getClusterId()));
if (capacityState == AllocationState.Enabled && nextState != ResourceState.Enabled){
capacityState = AllocationState.Disabled;
}
_capacityDao.updateCapacityState(null, null, null, host.getId(), capacityState.toString());
}
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 send MaintainCommand to host: " + hostId);
}
try {
resourceStateTransitTo(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<VMInstanceVO> vms = _vmDao.listByHostId(hostId);
if (vms.size() == 0) {
return true;
}
List<HostVO> hosts = listAllUpAndEnabledHosts(Host.Type.Routing, host.getClusterId(), host.getPodId(), host.getDataCenterId());
for (final VMInstanceVO vm : vms) {
if (hosts == null || hosts.isEmpty() || !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_PREPARE_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) throws NoTransitionException {
Long hostId = cmd.getId();
Long guestOSCategoryId = cmd.getOsCategoryId();
// Verify that the host exists
HostVO host = _hostDao.findById(hostId);
if (host == null) {
throw new InvalidParameterValueException("Host with id " + hostId + " doesn't exist");
}
if (cmd.getAllocationState() != null) {
ResourceState.Event resourceEvent = ResourceState.Event.toEvent(cmd.getAllocationState());
if (resourceEvent != ResourceState.Event.Enable && resourceEvent != ResourceState.Event.Disable) {
throw new CloudRuntimeException("Invalid allocation state:" + cmd.getAllocationState() + ", only Enable/Disable are allowed");
}
resourceStateTransitTo(host, resourceEvent, _nodeId);
}
if (guestOSCategoryId != null) {
// 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<String, String> 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);
}
List<String> hostTags = cmd.getHostTags();
if (hostTags != null) {
if(s_logger.isDebugEnabled()){
s_logger.debug("Updating Host Tags to :"+hostTags);
}
_hostTagsDao.persist(hostId, hostTags);
}
String url = cmd.getUrl();
if (url != null) {
_storageMgr.updateSecondaryStorage(cmd.getId(), cmd.getUrl());
}
HostVO updatedHost = _hostDao.findById(hostId);
return updatedHost;
}
@Override
public Cluster getCluster(Long clusterId) {
return _clusterDao.findById(clusterId);
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_name = name;
_defaultSystemVMHypervisor = HypervisorType.getType(_configDao.getValue(Config.SystemVMDefaultHypervisor.toString()));
return true;
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public String getName() {
return _name;
}
@Override
public List<HypervisorType> getSupportedHypervisorTypes(long zoneId, boolean forVirtualRouter, Long podId) {
List<HypervisorType> hypervisorTypes = new ArrayList<HypervisorType>();
List<ClusterVO> clustersForZone = new ArrayList<ClusterVO>();
if (podId != null) {
clustersForZone = _clusterDao.listByPodId(podId);
} else {
clustersForZone = _clusterDao.listByZoneId(zoneId);
}
for (ClusterVO cluster : clustersForZone) {
HypervisorType hType = cluster.getHypervisorType();
if (!forVirtualRouter || (forVirtualRouter && hType != HypervisorType.BareMetal && hType != HypervisorType.Ovm)) {
hypervisorTypes.add(hType);
}
}
return hypervisorTypes;
}
@Override
public HypervisorType getDefaultHypervisor(long zoneId) {
HypervisorType defaultHyper = HypervisorType.None;
if (_defaultSystemVMHypervisor != HypervisorType.None) {
defaultHyper = _defaultSystemVMHypervisor;
}
DataCenterVO dc = _dcDao.findById(zoneId);
if (dc == null) {
return HypervisorType.None;
}
_dcDao.loadDetails(dc);
String defaultHypervisorInZone = dc.getDetail("defaultSystemVMHypervisorType");
if (defaultHypervisorInZone != null) {
defaultHyper = HypervisorType.getType(defaultHypervisorInZone);
}
List<VMTemplateVO> systemTemplates = _templateDao.listAllSystemVMTemplates();
boolean isValid = false;
for (VMTemplateVO template : systemTemplates) {
if (template.getHypervisorType() == defaultHyper) {
isValid = true;
break;
}
}
if (isValid) {
List<ClusterVO> clusters = _clusterDao.listByDcHyType(zoneId, defaultHyper.toString());
if (clusters.size() <= 0) {
isValid = false;
}
}
if (isValid) {
return defaultHyper;
} else {
return HypervisorType.None;
}
}
@Override
public HypervisorType getAvailableHypervisor(long zoneId) {
HypervisorType defaultHype = getDefaultHypervisor(zoneId);
if (defaultHype == HypervisorType.None) {
List<HypervisorType> supportedHypes = getSupportedHypervisorTypes(zoneId, false, null);
if (supportedHypes.size() > 0) {
defaultHype = supportedHypes.get(0);
}
}
if (defaultHype == HypervisorType.None) {
defaultHype = HypervisorType.Any;
}
return defaultHype;
}
@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<String, ResourceStateAdapter> item = (Map.Entry<String, ResourceStateAdapter>) it.next();
ResourceStateAdapter adapter = item.getValue();
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<String, String>) args[3], (List<String>) 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<String, String> details, List<String> hostTags,
ResourceStateAdapter.Event stateEvent) {
StartupCommand startup = cmds[0];
HostVO host = findHostByGuid(startup.getGuid());
boolean isNew = false;
if (host == null) {
host = findHostByGuid(startup.getGuidWithoutResource());
}
if (host == null) {
host = new HostVO(startup.getGuid());
isNew = true;
}
String dataCenter = startup.getDataCenter();
String pod = startup.getPod();
String cluster = startup.getCluster();
if (pod != null && dataCenter != null && pod.equalsIgnoreCase("default") && dataCenter.equalsIgnoreCase("default")) {
List<HostPodVO> 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);
if (host == null) {
throw new CloudRuntimeException("No resource state adapter response");
}
if (isNew) {
host = _hostDao.persist(host);
} else {
_hostDao.update(host.getId(), host);
}
try {
resourceStateTransitTo(host, ResourceState.Event.InternalCreated, _nodeId);
/* Agent goes to Connecting status */
_agentMgr.agentStatusTransitTo(host, Status.Event.AgentConnected, _nodeId);
} catch (Exception e) {
s_logger.debug("Cannot transmit host " + host.getId() + " to Creating state", e);
_agentMgr.agentStatusTransitTo(host, Status.Event.Error, _nodeId);
try {
resourceStateTransitTo(host, ResourceState.Event.Error, _nodeId);
} catch (NoTransitionException e1) {
s_logger.debug("Cannot transmit host " + host.getId() + "to Error state", e);
}
}
return host;
}
private Host createHostAndAgent(ServerResource resource, Map<String, String> details, boolean old, List<String> hostTags,
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 = findHostByGuid(firstCmd.getGuid());
if (host == null) {
host = findHostByGuid(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);
/* Don't change resource state here since HostVO is already in database, which means resource state has had an appropriate value*/
}
}
}
return host;
}
@Override
public Host createHostAndAgent(Long hostId, ServerResource resource, Map<String, String> details, boolean old, List<String> hostTags, boolean forRebalance) {
_agentMgr.tapLoadingAgents(hostId, TapAgentsAction.Add);
Host host = createHostAndAgent(resource, details, old, hostTags, forRebalance);
_agentMgr.tapLoadingAgents(hostId, TapAgentsAction.Del);
return host;
}
@Override
public Host addHost(long zoneId, ServerResource resource, Type hostType, Map<String, String> 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<String, String> details = hostDetails;
String guid = details.get("guid");
List<HostVO> currentHosts = this.listAllUpAndEnabledHostsInOneZoneByType(hostType, zoneId);
for (HostVO currentHost : currentHosts) {
if (currentHost.getGuid().equals(guid)) {
return currentHost;
}
}
return createHostAndAgent(resource, hostDetails, true, 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<DataCenterIpAddressVO> 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<IPAddressVO> 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<String, String> details, List<String> 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<String, String> 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<VMInstanceVO> 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<VMInstanceVO> 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<VMInstanceVO> 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 {
resourceStateTransitTo(host, ResourceState.Event.AdminCancelMaintenance, _nodeId);
_agentMgr.pullAgentOutMaintenance(hostId);
//for kvm, need to log into kvm host, restart cloud-agent
if (host.getHypervisorType() == HypervisorType.KVM) {
_hostDao.loadDetails(host);
String password = host.getDetail("password");
String username = host.getDetail("username");
if (password == null || username == null) {
s_logger.debug("Can't find password/username");
return false;
}
com.trilead.ssh2.Connection connection = SSHCmdHelper.acquireAuthorizedConnection(host.getPrivateIpAddress(), 22, username, password);
if (connection == null) {
s_logger.debug("Failed to connect to host: " + host.getPrivateIpAddress());
return false;
}
try {
SSHCmdHelper.sshExecuteCmdOneShot(connection, "service cloud-agent restart");
} catch (sshException e) {
return false;
}
}
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) {
HostVO host = _hostDao.findById(hostId);
if (host == null) {
s_logger.debug("Cannot find host " + hostId + ", assuming it has been deleted, skip umanage");
return true;
}
if (host.getHypervisorType() == HypervisorType.KVM) {
MaintainAnswer answer = (MaintainAnswer) _agentMgr.easySend(hostId, new MaintainCommand());
}
_agentMgr.disconnectWithoutInvestigation(hostId, Event.ShutdownRequested);
return true;
}
@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 = _hostDetailsDao.findDetail(hostId, ApiConstants.USERNAME);
String username = nv.getValue();
nv = _hostDetailsDao.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<HostVO> hosts = this.listAllHostsInCluster(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(h.getId());
} 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 resourceStateTransitTo(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;
}
}
}
@Override
public List<HostVO> findDirectlyConnectedHosts() {
/* The resource column is not null for direct connected resource */
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getResource(), Op.NNULL);
sc.addAnd(sc.getEntity().getResourceState(), Op.NIN, ResourceState.Disabled);
return sc.list();
}
@Override
public List<HostVO> listAllUpAndEnabledHosts(Type type, Long clusterId, Long podId, long dcId) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
if (type != null) {
sc.addAnd(sc.getEntity().getType(), Op.EQ, type);
}
if (clusterId != null) {
sc.addAnd(sc.getEntity().getClusterId(), Op.EQ, clusterId);
}
if (podId != null) {
sc.addAnd(sc.getEntity().getPodId(), Op.EQ, podId);
}
sc.addAnd(sc.getEntity().getDataCenterId(), Op.EQ, dcId);
sc.addAnd(sc.getEntity().getStatus(), Op.EQ, Status.Up);
sc.addAnd(sc.getEntity().getResourceState(), Op.EQ, ResourceState.Enabled);
return sc.list();
}
@Override
public List<HostVO> listAllUpAndEnabledNonHAHosts(Type type, Long clusterId, Long podId, long dcId) {
String haTag = _haMgr.getHaTag();
return _hostDao.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId, haTag);
}
@Override
public List<HostVO> findHostByGuid(long dcId, String guid) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getDataCenterId(), Op.EQ, dcId);
sc.addAnd(sc.getEntity().getGuid(), Op.EQ, guid);
return sc.list();
}
@Override
public List<HostVO> listAllHostsInCluster(long clusterId) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getClusterId(), Op.EQ, clusterId);
return sc.list();
}
@Override
public List<HostVO> listHostsInClusterByStatus(long clusterId, Status status) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getClusterId(), Op.EQ, clusterId);
sc.addAnd(sc.getEntity().getStatus(), Op.EQ, status);
return sc.list();
}
@Override
public List<HostVO> listAllUpAndEnabledHostsInOneZoneByType(Type type, long dcId) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getType(), Op.EQ, type);
sc.addAnd(sc.getEntity().getDataCenterId(), Op.EQ, dcId);
sc.addAnd(sc.getEntity().getStatus(), Op.EQ, Status.Up);
sc.addAnd(sc.getEntity().getResourceState(), Op.EQ, ResourceState.Enabled);
return sc.list();
}
@Override
public List<HostVO> listAllNotInMaintenanceHostsInOneZone(Type type, Long dcId) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
if (dcId != null){
sc.addAnd(sc.getEntity().getDataCenterId(), Op.EQ, dcId);
}
sc.addAnd(sc.getEntity().getType(), Op.EQ, type);
sc.addAnd(sc.getEntity().getResourceState(), Op.NIN, ResourceState.Maintenance, ResourceState.ErrorInMaintenance, ResourceState.PrepareForMaintenance, ResourceState.Error);
return sc.list();
}
@Override
public List<HostVO> listAllHostsInOneZoneByType(Type type, long dcId) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getType(), Op.EQ, type);
sc.addAnd(sc.getEntity().getDataCenterId(), Op.EQ, dcId);
return sc.list();
}
@Override
public List<HostVO> listAllHostsInAllZonesByType(Type type) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getType(), Op.EQ, type);
return sc.list();
}
@Override
public List<HypervisorType> listAvailHypervisorInZone(Long hostId, Long zoneId) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
if (zoneId != null) {
sc.addAnd(sc.getEntity().getDataCenterId(), Op.EQ, zoneId);
}
if (hostId != null) {
sc.addAnd(sc.getEntity().getId(), Op.EQ, hostId);
}
sc.addAnd(sc.getEntity().getType(), Op.EQ, Host.Type.Routing);
List<HostVO> hosts = sc.list();
List<HypervisorType> hypers = new ArrayList<HypervisorType>(5);
for (HostVO host : hosts) {
hypers.add(host.getHypervisorType());
}
return hypers;
}
@Override
public HostVO findHostByGuid(String guid) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getGuid(), Op.EQ, guid);
return sc.find();
}
@Override
public HostVO findHostByName(String name) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getName(), Op.EQ, name);
return sc.find();
}
@Override
public List<HostVO> listHostsByNameLike(String name) {
SearchCriteriaService<HostVO, HostVO> sc = SearchCriteria2.create(HostVO.class);
sc.addAnd(sc.getEntity().getName(), Op.LIKE, "%" + name + "%");
return sc.list();
}
@Override
public Pair<HostPodVO, Long> findPod(VirtualMachineTemplate template, ServiceOfferingVO offering, DataCenterVO dc, long accountId, Set<Long> avoids) {
final Enumeration en = _podAllocators.enumeration();
while (en.hasMoreElements()) {
final PodAllocator allocator = (PodAllocator) en.nextElement();
final Pair<HostPodVO, Long> pod = allocator.allocateTo(template, offering, dc, accountId, avoids);
if (pod != null) {
return pod;
}
}
return null;
}
@Override
public HostStats getHostStatistics(long hostId) {
Answer answer = _agentMgr.easySend(hostId, new GetHostStatsCommand(_hostDao.findById(hostId).getGuid(), _hostDao.findById(hostId).getName(), hostId));
if (answer != null && (answer instanceof UnsupportedAnswer)) {
return null;
}
if (answer == null || !answer.getResult()) {
String msg = "Unable to obtain host " + hostId + " statistics. ";
s_logger.warn(msg);
return null;
} else {
// now construct the result object
if (answer instanceof GetHostStatsAnswer) {
return ((GetHostStatsAnswer) answer).getHostStats();
}
}
return null;
}
@Override
public Long getGuestOSCategoryId(long hostId) {
HostVO host = _hostDao.findById(hostId);
if (host == null) {
return null;
} else {
_hostDao.loadDetails(host);
DetailVO detail = _hostDetailsDao.findDetail(hostId, "guest.os.category.id");
if (detail == null) {
return null;
} else {
return Long.parseLong(detail.getValue());
}
}
}
@Override
public String getHostTags(long hostId) {
List<String> hostTags = _hostTagsDao.gethostTags(hostId);
if (hostTags == null) {
return null;
} else {
return StringUtils.listToCsvTags(hostTags);
}
}
@Override
public List<PodCluster> listByDataCenter(long dcId) {
List<HostPodVO> pods = _podDao.listByDataCenterId(dcId);
ArrayList<PodCluster> pcs = new ArrayList<PodCluster>();
for (HostPodVO pod : pods) {
List<ClusterVO> clusters = _clusterDao.listByPodId(pod.getId());
if (clusters.size() == 0) {
pcs.add(new PodCluster(pod, null));
} else {
for (ClusterVO cluster : clusters) {
pcs.add(new PodCluster(pod, cluster));
}
}
}
return pcs;
}
}