This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 92cc4514eae9f5f9c24ec3a2befb1c23ae5d6f28
Merge: 3424d9e 13c81a8
Author: Rohit Yadav <rohit.ya...@shapeblue.com>
AuthorDate: Fri Dec 28 15:20:23 2018 +0530

    Merge remote-tracking branch 'origin/4.11'
    
    Signed-off-by: Rohit Yadav <rohit.ya...@shapeblue.com>

 .../java/com/cloud/resource/ResourceManager.java   | 10 ++++-
 .../com/cloud/resource/ResourceManagerImpl.java    | 49 ++++++++++++++++++++--
 .../cloud/resource/MockResourceManagerImpl.java    | 11 +++++
 .../cloud/resource/ResourceManagerImplTest.java    | 18 ++++++++
 4 files changed, 84 insertions(+), 4 deletions(-)

diff --cc server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
index 61b7ebf,0000000..8bc97cb
mode 100755,000000..100755
--- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
@@@ -1,2911 -1,0 +1,2954 @@@
 +// Licensed to the Apache Software Foundation (ASF) under one
 +// or more contributor license agreements.  See the NOTICE file
 +// distributed with this work for additional information
 +// regarding copyright ownership.  The ASF licenses this file
 +// to you under the Apache License, Version 2.0 (the
 +// "License"); you may not use this file except in compliance
 +// with the License.  You may obtain a copy of the License at
 +//
 +//   http://www.apache.org/licenses/LICENSE-2.0
 +//
 +// Unless required by applicable law or agreed to in writing,
 +// software distributed under the License is distributed on an
 +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 +// KIND, either express or implied.  See the License for the
 +// specific language governing permissions and limitations
 +// under the License.
 +package com.cloud.resource;
 +
 +import java.net.URI;
 +import java.net.URISyntaxException;
 +import java.net.URLDecoder;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.Iterator;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Random;
++import java.util.concurrent.ConcurrentHashMap;
 +
 +import javax.inject.Inject;
 +import javax.naming.ConfigurationException;
 +
 +import com.cloud.vm.dao.UserVmDetailsDao;
++import org.apache.cloudstack.framework.config.ConfigKey;
++import org.apache.commons.lang.ObjectUtils;
++import org.apache.log4j.Logger;
++import org.springframework.stereotype.Component;
++
 +import org.apache.cloudstack.api.ApiConstants;
 +import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
 +import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
 +import org.apache.cloudstack.api.command.admin.host.AddHostCmd;
 +import org.apache.cloudstack.api.command.admin.host.AddSecondaryStorageCmd;
 +import org.apache.cloudstack.api.command.admin.host.CancelMaintenanceCmd;
 +import org.apache.cloudstack.api.command.admin.host.PrepareForMaintenanceCmd;
 +import org.apache.cloudstack.api.command.admin.host.ReconnectHostCmd;
 +import org.apache.cloudstack.api.command.admin.host.UpdateHostCmd;
 +import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd;
 +import org.apache.cloudstack.context.CallContext;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 +import org.apache.cloudstack.utils.identity.ManagementServerNode;
 +import org.apache.commons.collections.CollectionUtils;
- import org.apache.commons.lang.ObjectUtils;
- import org.apache.log4j.Logger;
- import org.springframework.stereotype.Component;
 +
 +import com.cloud.agent.AgentManager;
 +import com.cloud.agent.api.Answer;
 +import com.cloud.agent.api.Command;
 +import com.cloud.agent.api.GetVncPortCommand;
 +import com.cloud.agent.api.GetVncPortAnswer;
 +import com.cloud.agent.api.GetGPUStatsAnswer;
 +import com.cloud.agent.api.GetGPUStatsCommand;
 +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.PropagateResourceEventCommand;
 +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.api.VgpuTypesInfo;
 +import com.cloud.agent.api.to.GPUDeviceTO;
 +import com.cloud.agent.transport.Request;
 +import com.cloud.capacity.Capacity;
 +import com.cloud.capacity.CapacityManager;
 +import com.cloud.capacity.CapacityState;
 +import com.cloud.capacity.CapacityVO;
 +import com.cloud.capacity.dao.CapacityDao;
 +import com.cloud.cluster.ClusterManager;
 +import com.cloud.configuration.Config;
 +import com.cloud.configuration.ConfigurationManager;
 +import com.cloud.dc.ClusterDetailsDao;
 +import com.cloud.dc.ClusterDetailsVO;
 +import com.cloud.dc.ClusterVO;
 +import com.cloud.dc.DataCenter;
 +import com.cloud.dc.DataCenter.NetworkType;
 +import com.cloud.dc.DataCenterIpAddressVO;
 +import com.cloud.dc.DataCenterVO;
 +import com.cloud.dc.DedicatedResourceVO;
 +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.DedicatedResourceDao;
 +import com.cloud.dc.dao.HostPodDao;
 +import com.cloud.deploy.PlannerHostReservationVO;
 +import com.cloud.deploy.dao.PlannerHostReservationDao;
 +import com.cloud.event.ActionEvent;
 +import com.cloud.event.ActionEventUtils;
 +import com.cloud.event.EventTypes;
 +import com.cloud.event.EventVO;
 +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.gpu.GPU;
 +import com.cloud.gpu.HostGpuGroupsVO;
 +import com.cloud.gpu.VGPUTypesVO;
 +import com.cloud.gpu.dao.HostGpuGroupsDao;
 +import com.cloud.gpu.dao.VGPUTypesDao;
 +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.discoverer.KvmDummyResourceBase;
 +import com.cloud.network.dao.IPAddressDao;
 +import com.cloud.network.dao.IPAddressVO;
 +import com.cloud.org.Cluster;
 +import com.cloud.org.Grouping;
 +import com.cloud.org.Managed;
 +import com.cloud.serializer.GsonHelper;
 +import com.cloud.service.dao.ServiceOfferingDetailsDao;
 +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.StorageService;
 +import com.cloud.storage.VMTemplateVO;
 +import com.cloud.storage.dao.GuestOSCategoryDao;
 +import com.cloud.storage.dao.StoragePoolHostDao;
 +import com.cloud.storage.dao.VMTemplateDao;
 +import com.cloud.user.Account;
 +import com.cloud.user.AccountManager;
 +import com.cloud.utils.StringUtils;
 +import com.cloud.utils.UriUtils;
 +import com.cloud.utils.component.Manager;
 +import com.cloud.utils.component.ManagerBase;
 +import com.cloud.utils.db.DB;
 +import com.cloud.utils.db.Filter;
 +import com.cloud.utils.db.GenericSearchBuilder;
 +import com.cloud.utils.db.GlobalLock;
 +import com.cloud.utils.db.JoinBuilder;
 +import com.cloud.utils.db.QueryBuilder;
 +import com.cloud.utils.db.SearchBuilder;
 +import com.cloud.utils.db.SearchCriteria;
 +import com.cloud.utils.db.SearchCriteria.Func;
 +import com.cloud.utils.db.SearchCriteria.Op;
 +import com.cloud.utils.db.Transaction;
 +import com.cloud.utils.db.TransactionCallback;
 +import com.cloud.utils.db.TransactionCallbackNoReturn;
 +import com.cloud.utils.db.TransactionLegacy;
 +import com.cloud.utils.db.TransactionStatus;
 +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;
 +import com.cloud.vm.VirtualMachine.State;
 +import com.cloud.vm.VirtualMachineManager;
 +import com.cloud.vm.dao.VMInstanceDao;
 +import com.google.gson.Gson;
 +
 +@Component
 +public class ResourceManagerImpl extends ManagerBase implements 
ResourceManager, ResourceService, Manager {
 +    private static final Logger s_logger = 
Logger.getLogger(ResourceManagerImpl.class);
 +
 +    Gson _gson;
 +
 +    @Inject
 +    private AccountManager _accountMgr;
 +    @Inject
 +    private AgentManager _agentMgr;
 +    @Inject
 +    private StorageManager _storageMgr;
 +    @Inject
 +    private DataCenterDao _dcDao;
 +    @Inject
 +    private HostPodDao _podDao;
 +    @Inject
 +    private ClusterDetailsDao _clusterDetailsDao;
 +    @Inject
 +    private ClusterDao _clusterDao;
 +    @Inject
 +    private CapacityDao _capacityDao;
 +    @Inject
 +    private HostDao _hostDao;
 +    @Inject
 +    private HostDetailsDao _hostDetailsDao;
 +    @Inject
 +    private ConfigurationDao _configDao;
 +    @Inject
 +    private HostTagsDao _hostTagsDao;
 +    @Inject
 +    private GuestOSCategoryDao _guestOSCategoryDao;
 +    @Inject
 +    protected HostGpuGroupsDao _hostGpuGroupsDao;
 +    @Inject
 +    protected VGPUTypesDao _vgpuTypesDao;
 +    @Inject
 +    private PrimaryDataStoreDao _storagePoolDao;
 +    @Inject
 +    private DataCenterIpAddressDao _privateIPAddressDao;
 +    @Inject
 +    private IPAddressDao _publicIPAddressDao;
 +    @Inject
 +    private VirtualMachineManager _vmMgr;
 +    @Inject
 +    private VMInstanceDao _vmDao;
 +    @Inject
 +    private HighAvailabilityManager _haMgr;
 +    @Inject
 +    private StorageService _storageSvr;
 +    @Inject
 +    PlannerHostReservationDao _plannerHostReserveDao;
 +    @Inject
 +    private DedicatedResourceDao _dedicatedDao;
 +    @Inject
 +    private ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
 +
 +    private List<? extends Discoverer> _discoverers;
 +
 +    public List<? extends Discoverer> getDiscoverers() {
 +        return _discoverers;
 +    }
 +
 +    public void setDiscoverers(final List<? extends Discoverer> discoverers) {
 +        _discoverers = discoverers;
 +    }
 +
 +    @Inject
 +    private ClusterManager _clusterMgr;
 +    @Inject
 +    private StoragePoolHostDao _storagePoolHostDao;
 +
 +    @Inject
 +    private VMTemplateDao _templateDao;
 +    @Inject
 +    private ConfigurationManager _configMgr;
 +    @Inject
 +    private ClusterVSMMapDao _clusterVSMMapDao;
 +    @Inject
 +    private UserVmDetailsDao userVmDetailsDao;
 +
 +    private final long _nodeId = ManagementServerNode.getManagementServerId();
 +
 +    private final HashMap<String, ResourceStateAdapter> 
_resourceStateAdapters = new HashMap<String, ResourceStateAdapter>();
 +
 +    private final HashMap<Integer, List<ResourceListener>> 
_lifeCycleListeners = new HashMap<Integer, List<ResourceListener>>();
 +    private HypervisorType _defaultSystemVMHypervisor;
 +
 +    private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 
30; // seconds
 +
 +    private GenericSearchBuilder<HostVO, String> _hypervisorsInDC;
 +
 +    private SearchBuilder<HostGpuGroupsVO> _gpuAvailability;
 +
++    private Map<Long,Integer> retryHostMaintenance = new 
ConcurrentHashMap<>();
++
 +    private void insertListener(final Integer event, final 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(final Integer event, final 
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(final ResourceListener listener) {
 +        synchronized (_lifeCycleListeners) {
 +            final Iterator it = _lifeCycleListeners.entrySet().iterator();
 +            while (it.hasNext()) {
 +                final Map.Entry<Integer, List<ResourceListener>> items = 
(Map.Entry<Integer, List<ResourceListener>>)it.next();
 +                final List<ResourceListener> lst = items.getValue();
 +                lst.remove(listener);
 +            }
 +        }
 +    }
 +
 +    protected void processResourceEvent(final Integer event, final Object... 
params) {
 +        final List<ResourceListener> lst = _lifeCycleListeners.get(event);
 +        if (lst == null || lst.size() == 0) {
 +            return;
 +        }
 +
 +        String eventName;
 +        for (final ResourceListener l : lst) {
 +            if (event.equals(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.equals(ResourceListener.EVENT_DISCOVER_AFTER)) {
 +                l.processDiscoverEventAfter((Map<? extends ServerResource, 
Map<String, String>>)params[0]);
 +                eventName = "EVENT_DISCOVER_AFTER";
 +            } else if 
(event.equals(ResourceListener.EVENT_DELETE_HOST_BEFORE)) {
 +                l.processDeleteHostEventBefore((HostVO)params[0]);
 +                eventName = "EVENT_DELETE_HOST_BEFORE";
 +            } else if 
(event.equals(ResourceListener.EVENT_DELETE_HOST_AFTER)) {
 +                l.processDeletHostEventAfter((HostVO)params[0]);
 +                eventName = "EVENT_DELETE_HOST_AFTER";
 +            } else if 
(event.equals(ResourceListener.EVENT_CANCEL_MAINTENANCE_BEFORE)) {
 +                l.processCancelMaintenaceEventBefore((Long)params[0]);
 +                eventName = "EVENT_CANCEL_MAINTENANCE_BEFORE";
 +            } else if 
(event.equals(ResourceListener.EVENT_CANCEL_MAINTENANCE_AFTER)) {
 +                l.processCancelMaintenaceEventAfter((Long)params[0]);
 +                eventName = "EVENT_CANCEL_MAINTENANCE_AFTER";
 +            } else if 
(event.equals(ResourceListener.EVENT_PREPARE_MAINTENANCE_BEFORE)) {
 +                l.processPrepareMaintenaceEventBefore((Long)params[0]);
 +                eventName = "EVENT_PREPARE_MAINTENANCE_BEFORE";
 +            } else if 
(event.equals(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(final AddClusterCmd cmd) 
throws IllegalArgumentException, DiscoveryException, ResourceInUseException {
 +        final long dcId = cmd.getZoneId();
 +        final long podId = cmd.getPodId();
 +        final String clusterName = cmd.getClusterName();
 +        String url = cmd.getUrl();
 +        final String username = cmd.getUsername();
 +        final String password = cmd.getPassword();
 +
 +        if (url != null) {
 +            url = URLDecoder.decode(url);
 +        }
 +
 +        URI uri = null;
 +
 +        // Check if the zone exists in the system
 +        final DataCenterVO zone = _dcDao.findById(dcId);
 +        if (zone == null) {
 +            final InvalidParameterValueException ex = new 
InvalidParameterValueException("Can't find zone by the id specified");
 +            ex.addProxyObject(String.valueOf(dcId), "dcId");
 +            throw ex;
 +        }
 +
 +        final Account account = CallContext.current().getCallingAccount();
 +        if (Grouping.AllocationState.Disabled == zone.getAllocationState() && 
!_accountMgr.isRootAdmin(account.getId())) {
 +            final PermissionDeniedException ex = new 
PermissionDeniedException("Cannot perform this operation, Zone with specified 
id is currently disabled");
 +            ex.addProxyObject(zone.getUuid(), "dcId");
 +            throw ex;
 +        }
 +
 +        final 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)) {
 +            final InvalidParameterValueException ex = new 
InvalidParameterValueException("Pod with specified id doesn't belong to the 
zone " + dcId);
 +            ex.addProxyObject(pod.getUuid(), "podId");
 +            ex.addProxyObject(zone.getUuid(), "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");
 +        }
 +
 +        final 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 ");
 +        }
 +
 +        if (zone.isSecurityGroupEnabled() && 
zone.getNetworkType().equals(NetworkType.Advanced)) {
 +            if (hypervisorType != HypervisorType.KVM && hypervisorType != 
HypervisorType.XenServer
 +                    && hypervisorType != HypervisorType.LXC && hypervisorType 
!= HypervisorType.Simulator) {
 +                throw new InvalidParameterValueException("Don't support 
hypervisor type " + hypervisorType + " in advanced security enabled zone");
 +            }
 +        }
 +
 +        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 (final IllegalArgumentException ex) {
 +                throw new InvalidParameterValueException("Unable to resolve 
Allocation State '" + cmd.getAllocationState() + "' to a supported state");
 +            }
 +        }
 +        if (allocationState == null) {
 +            allocationState = Grouping.AllocationState.Enabled;
 +        }
 +
 +        final Discoverer discoverer = getMatchingDiscover(hypervisorType);
 +        if (discoverer == null) {
 +
 +            throw new InvalidParameterValueException("Could not find 
corresponding resource manager for " + cmd.getHypervisor());
 +        }
 +
 +        if (hypervisorType == HypervisorType.VMware) {
 +            final Map<String, String> allParams = cmd.getFullUrlParams();
 +            discoverer.putParam(allParams);
 +        }
 +
 +        final List<ClusterVO> result = new ArrayList<ClusterVO>();
 +
 +        ClusterVO cluster = new ClusterVO(dcId, podId, clusterName);
 +        cluster.setHypervisorType(hypervisorType.toString());
 +
 +        cluster.setClusterType(clusterType);
 +        cluster.setAllocationState(allocationState);
 +        try {
 +            cluster = _clusterDao.persist(cluster);
 +        } catch (final Exception e) {
 +            // no longer tolerate exception during the cluster creation phase
 +            final 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.getUuid(), "podId");
 +            ex.addProxyObject(zone.getUuid(), "dcId");
 +            throw ex;
 +        }
 +        result.add(cluster);
 +
 +        if (clusterType == Cluster.ClusterType.CloudManaged) {
 +            final Map<String, String> details = new HashMap<String, String>();
 +            // should do this nicer perhaps ?
 +            if (hypervisorType == HypervisorType.Ovm3) {
 +                final Map<String, String> allParams = cmd.getFullUrlParams();
 +                details.put("ovm3vip", allParams.get("ovm3vip"));
 +                details.put("ovm3pool", allParams.get("ovm3pool"));
 +                details.put("ovm3cluster", allParams.get("ovm3cluster"));
 +            }
 +            details.put("cpuOvercommitRatio", 
CapacityManager.CpuOverprovisioningFactor.value().toString());
 +            details.put("memoryOvercommitRatio", 
CapacityManager.MemOverprovisioningFactor.value().toString());
 +            _clusterDetailsDao.persist(cluster.getId(), details);
 +            return result;
 +        }
 +
 +        // save cluster details for later cluster/host cross-checking
 +        final Map<String, String> details = new HashMap<String, String>();
 +        details.put("url", url);
 +        details.put("username", username);
 +        details.put("password", password);
 +        details.put("cpuOvercommitRatio", 
CapacityManager.CpuOverprovisioningFactor.value().toString());
 +        details.put("memoryOvercommitRatio", 
CapacityManager.MemOverprovisioningFactor.value().toString());
 +        _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 (final URISyntaxException e) {
 +                throw new InvalidParameterValueException(url + " is not a 
valid uri");
 +            }
 +
 +            final List<HostVO> hosts = new ArrayList<HostVO>();
 +            Map<? extends ServerResource, Map<String, String>> resources = 
null;
 +            resources = discoverer.find(dcId, podId, cluster.getId(), uri, 
username, password, null);
 +
 +            if (resources != null) {
 +                for (final Map.Entry<? extends ServerResource, Map<String, 
String>> entry : resources.entrySet()) {
 +                    final ServerResource resource = entry.getKey();
 +
 +                    final 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(cluster.getId());
 +                _clusterDao.remove(cluster.getId());
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public Discoverer getMatchingDiscover(final Hypervisor.HypervisorType 
hypervisorType) {
 +        for (final Discoverer discoverer : _discoverers) {
 +            if (discoverer.getHypervisorType() == hypervisorType) {
 +                return discoverer;
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public List<? extends Host> discoverHosts(final AddHostCmd cmd) throws 
IllegalArgumentException, DiscoveryException, InvalidParameterValueException {
 +        Long dcId = cmd.getZoneId();
 +        final Long podId = cmd.getPodId();
 +        final Long clusterId = cmd.getClusterId();
 +        String clusterName = cmd.getClusterName();
 +        final String url = cmd.getUrl();
 +        final String username = cmd.getUsername();
 +        final String password = cmd.getPassword();
 +        final List<String> hostTags = cmd.getHostTags();
 +
 +        dcId = 
_accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(),
 dcId);
 +
 +        // this is for standalone option
 +        if (clusterName == null && clusterId == null) {
 +            clusterName = "Standalone-" + url;
 +        }
 +
 +        if (clusterId != null) {
 +            final ClusterVO cluster = _clusterDao.findById(clusterId);
 +            if (cluster == null) {
 +                final InvalidParameterValueException ex = new 
InvalidParameterValueException("can not find cluster for specified clusterId");
 +                ex.addProxyObject(clusterId.toString(), "clusterId");
 +                throw ex;
 +            } else {
 +                if (cluster.getGuid() == null) {
 +                    final List<HostVO> hosts = 
listAllHostsInCluster(clusterId);
 +                    if (!hosts.isEmpty()) {
 +                        final 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.getUuid(), "clusterId");
 +                        throw ex;
 +                    }
 +                }
 +            }
 +        }
 +
 +        return discoverHostsFull(dcId, podId, clusterId, clusterName, url, 
username, password, cmd.getHypervisor(), hostTags, cmd.getFullUrlParams(), 
false);
 +    }
 +
 +    @Override
 +    public List<? extends Host> discoverHosts(final AddSecondaryStorageCmd 
cmd) throws IllegalArgumentException, DiscoveryException, 
InvalidParameterValueException {
 +        final Long dcId = cmd.getZoneId();
 +        final String url = cmd.getUrl();
 +        return discoverHostsFull(dcId, null, null, null, url, null, null, 
"SecondaryStorage", null, null, false);
 +    }
 +
 +    private List<HostVO> discoverHostsFull(final Long dcId, final Long podId, 
Long clusterId, final String clusterName, String url, String username, String 
password,
 +            final String hypervisorType, final List<String> hostTags, final 
Map<String, String> params, final boolean deferAgentCreation) throws 
IllegalArgumentException, DiscoveryException,
 +            InvalidParameterValueException {
 +        URI uri = null;
 +
 +        // Check if the zone exists in the system
 +        final DataCenterVO zone = _dcDao.findById(dcId);
 +        if (zone == null) {
 +            throw new InvalidParameterValueException("Can't find zone by id " 
+ dcId);
 +        }
 +
 +        final Account account = CallContext.current().getCallingAccount();
 +        if (Grouping.AllocationState.Disabled == zone.getAllocationState() && 
!_accountMgr.isRootAdmin(account.getId())) {
 +            final PermissionDeniedException ex = new 
PermissionDeniedException("Cannot perform this operation, Zone with specified 
id is currently disabled");
 +            ex.addProxyObject(zone.getUuid(), "dcId");
 +            throw ex;
 +        }
 +
 +        // Check if the pod exists in the system
 +        if (podId != null) {
 +            final 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)) {
 +                final InvalidParameterValueException ex =
 +                        new InvalidParameterValueException("Pod with 
specified podId" + podId + " doesn't belong to the zone with specified zoneId" 
+ dcId);
 +                ex.addProxyObject(pod.getUuid(), "podId");
 +                ex.addProxyObject(zone.getUuid(), "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
 +                final Map<String, String> clusterDetails = 
_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 (final URISyntaxException e) {
 +                    throw new InvalidParameterValueException(url + " is not a 
valid uri");
 +                }
 +            }
 +        }
 +
 +        if 
((hypervisorType.equalsIgnoreCase(HypervisorType.BareMetal.toString()))) {
 +            if (hostTags.isEmpty()) {
 +                throw new InvalidParameterValueException("hosttag is 
mandatory while adding host of type Baremetal");
 +            }
 +        }
 +
 +        if (clusterName != null) {
 +            final 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 (final Exception e) {
 +                cluster = _clusterDao.findBy(clusterName, podId);
 +                if (cluster == null) {
 +                    final CloudRuntimeException ex =
 +                            new CloudRuntimeException("Unable to create 
cluster " + clusterName + " in pod with specified podId and data center with 
specified dcID", e);
 +                    ex.addProxyObject(pod.getUuid(), "podId");
 +                    ex.addProxyObject(zone.getUuid(), "dcId");
 +                    throw ex;
 +                }
 +            }
 +            clusterId = cluster.getId();
 +            if (_clusterDetailsDao.findDetail(clusterId, 
"cpuOvercommitRatio") == null) {
 +                final ClusterDetailsVO cluster_cpu_detail = new 
ClusterDetailsVO(clusterId, "cpuOvercommitRatio", "1");
 +                final ClusterDetailsVO cluster_memory_detail = new 
ClusterDetailsVO(clusterId, "memoryOvercommitRatio", "1");
 +                _clusterDetailsDao.persist(cluster_cpu_detail);
 +                _clusterDetailsDao.persist(cluster_memory_detail);
 +            }
 +
 +        }
 +
 +        try {
 +            uri = new URI(UriUtils.encodeURIComponent(url));
 +            if (uri.getScheme() == null) {
 +                throw new InvalidParameterValueException("uri.scheme is null 
" + url + ", add nfs:// (or cifs://) 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");
 +                }
 +            } else if (uri.getScheme().equalsIgnoreCase("cifs")) {
 +                // Don't validate against a URI encoded URI.
 +                final URI cifsUri = new URI(url);
 +                final String warnMsg = 
UriUtils.getCifsUriParametersProblems(cifsUri);
 +                if (warnMsg != null) {
 +                    throw new InvalidParameterValueException(warnMsg);
 +                }
 +            }
 +        } catch (final URISyntaxException e) {
 +            throw new InvalidParameterValueException(url + " is not a valid 
uri");
 +        }
 +
 +        final List<HostVO> hosts = new ArrayList<HostVO>();
 +        s_logger.info("Trying to add a new host at " + url + " in data center 
" + dcId);
 +        boolean isHypervisorTypeSupported = false;
 +        for (final Discoverer discoverer : _discoverers) {
 +            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 (final DiscoveryException e) {
 +                throw e;
 +            } catch (final 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 (final Map.Entry<? extends ServerResource, Map<String, 
String>> entry : resources.entrySet()) {
 +                    final 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) {
 +                        final Map<String, String> details = entry.getValue();
 +                        final String guid = details.get("guid");
 +                        final List<HostVO> kvmHosts = 
listAllUpAndEnabledHosts(Host.Type.Routing, clusterId, podId, dcId);
 +                        for (final 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);
 +
 +                                
_agentMgr.notifyMonitorsOfNewlyAddedHost(host.getId());
 +
 +                                return hosts;
 +                            }
 +                        }
 +                        return null;
 +                    }
 +
 +                    HostVO host = null;
 +                    if (deferAgentCreation) {
 +                        host = (HostVO)createHostAndAgentDeferred(resource, 
entry.getValue(), true, hostTags, false);
 +                    } else {
 +                        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) {
 +            final 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(final long hostId) {
 +        return _hostDao.findById(hostId);
 +    }
 +
 +    @DB
 +    protected boolean doDeleteHost(final long hostId, final boolean isForced, 
final boolean isForceDeleteStorage) {
 +        _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
 +        // Verify that host exists
 +        final HostVO host = _hostDao.findById(hostId);
 +        if (host == null) {
 +            throw new InvalidParameterValueException("Host with id " + hostId 
+ " doesn't exist");
 +        }
 +        
_accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(),
 host.getDataCenterId());
 +
 +        if (!isForced && host.getResourceState() != 
ResourceState.Maintenance) {
 +            throw new CloudRuntimeException("Host " + host.getUuid() +
 +                    " cannot be deleted as it is not in maintenance mode. 
Either put the host into maintenance or perform a forced deletion.");
 +        }
 +        // Get storage pool host mappings here because they can be removed as 
a
 +        // part of handleDisconnect later
 +        final List<StoragePoolHostVO> pools = 
_storagePoolHostDao.listByHostIdIncludingRemoved(hostId);
 +
 +        final ResourceStateAdapter.DeleteHostAnswer answer =
 +                
(ResourceStateAdapter.DeleteHostAnswer)dispatchToStateAdapters(ResourceStateAdapter.Event.DELETE_HOST,
 false, host, isForced,
 +                        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;
 +        }
 +
 +        Long clusterId = host.getClusterId();
 +
 +        _agentMgr.notifyMonitorsOfHostAboutToBeRemoved(host.getId());
 +
 +        Transaction.execute(new TransactionCallbackNoReturn() {
 +            @Override
 +            public void doInTransactionWithoutResult(final TransactionStatus 
status) {
 +                _dcDao.releasePrivateIpAddress(host.getPrivateIpAddress(), 
host.getDataCenterId(), null);
 +                _agentMgr.disconnectWithoutInvestigation(hostId, 
Status.Event.Remove);
 +
 +                // delete host details
 +                _hostDetailsDao.deleteDetails(hostId);
 +
 +                // if host is GPU enabled, delete GPU entries
 +                _hostGpuGroupsDao.deleteGpuEntries(hostId);
 +
 +                // delete host tags
 +                _hostTagsDao.deleteTags(hostId);
 +
 +                host.setGuid(null);
 +                final Long clusterId = host.getClusterId();
 +                host.setClusterId(null);
 +                _hostDao.update(host.getId(), host);
 +
 +                _hostDao.remove(hostId);
 +                if (clusterId != null) {
 +                    final List<HostVO> hosts = 
listAllHostsInCluster(clusterId);
 +                    if (hosts.size() == 0) {
 +                        final ClusterVO cluster = 
_clusterDao.findById(clusterId);
 +                        cluster.setGuid(null);
 +                        _clusterDao.update(clusterId, cluster);
 +                    }
 +                }
 +
 +                try {
 +                    resourceStateTransitTo(host, 
ResourceState.Event.DeleteHost, _nodeId);
 +                } catch (final 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);
 +
 +                // Make sure any VMs that were marked as being on this host 
are cleaned up
 +                final List<VMInstanceVO> vms = _vmDao.listByHostId(hostId);
 +                for (final VMInstanceVO vm : vms) {
 +                    // this is how VirtualMachineManagerImpl does it when it 
syncs VM states
 +                    vm.setState(State.Stopped);
 +                    vm.setHostId(null);
 +                    _vmDao.persist(vm);
 +                }
 +
 +                // For pool ids you got, delete local storage host entries in 
pool table
 +                // where
 +                for (final StoragePoolHostVO pool : pools) {
 +                    final Long poolId = pool.getPoolId();
 +                    final 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
 +                final Object[] capacityTypes = {Capacity.CAPACITY_TYPE_CPU, 
Capacity.CAPACITY_TYPE_MEMORY};
 +                final SearchCriteria<CapacityVO> hostCapacitySC = 
_capacityDao.createSearchCriteria();
 +                hostCapacitySC.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, 
hostId);
 +                hostCapacitySC.addAnd("capacityType", SearchCriteria.Op.IN, 
capacityTypes);
 +                _capacityDao.remove(hostCapacitySC);
 +                // remove from dedicated resources
 +                final DedicatedResourceVO dr = 
_dedicatedDao.findByHostId(hostId);
 +                if (dr != null) {
 +                    _dedicatedDao.remove(dr.getId());
 +                }
 +            }
 +        });
 +
 +        if (clusterId != null) {
 +            _agentMgr.notifyMonitorsOfRemovedHost(host.getId(), clusterId);
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean deleteHost(final long hostId, final boolean isForced, 
final boolean isForceDeleteStorage) {
 +        try {
 +            final Boolean result = propagateResourceEvent(hostId, 
ResourceState.Event.DeleteHost);
 +            if (result != null) {
 +                return result;
 +            }
 +        } catch (final AgentUnavailableException e) {
 +            return false;
 +        }
 +
 +        return doDeleteHost(hostId, isForced, isForceDeleteStorage);
 +    }
 +
 +    @Override
 +    @DB
 +    public boolean deleteCluster(final DeleteClusterCmd cmd) {
 +        try {
 +            Transaction.execute(new TransactionCallbackNoReturn() {
 +                @Override
 +                public void doInTransactionWithoutResult(final 
TransactionStatus status) {
 +                    final 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.");
 +                        }
 +                        throw new CloudRuntimeException("Cluster: " + 
cmd.getId() + " does not exist");
 +                    }
 +
 +                    final Hypervisor.HypervisorType hypervisorType = 
cluster.getHypervisorType();
 +
 +                    final 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");
 +                        }
 +                        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
 +                    final 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");
 +                        }
 +                        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());
 +                        }
 +                        // remove from dedicated resources
 +                        final DedicatedResourceVO dr = 
_dedicatedDao.findByClusterId(cluster.getId());
 +                        if (dr != null) {
 +                            _dedicatedDao.remove(dr.getId());
 +                        }
 +                    }
 +
 +                }
 +            });
 +            return true;
 +        } catch (final CloudRuntimeException e) {
 +            throw e;
 +        } catch (final Throwable t) {
 +            s_logger.error("Unable to delete cluster: " + cmd.getId(), t);
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    @DB
 +    public Cluster updateCluster(final Cluster clusterToUpdate, final String 
clusterType, final String hypervisor, final String allocationState, final 
String managedstate) {
 +
 +        final ClusterVO cluster = (ClusterVO)clusterToUpdate;
 +        // Verify cluster information and update the cluster if needed
 +        boolean doUpdate = false;
 +
 +        if (hypervisor != null && !hypervisor.isEmpty()) {
 +            final 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 (final 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 (final 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;
 +        final Managed.ManagedState oldManagedState = 
cluster.getManagedState();
 +        if (managedstate != null && !managedstate.isEmpty()) {
 +            try {
 +                newManagedState = Managed.ManagedState.valueOf(managedstate);
 +            } catch (final 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) {
 +            _clusterDao.update(cluster.getId(), cluster);
 +        }
 +
 +        if (newManagedState != null && 
!newManagedState.equals(oldManagedState)) {
 +            if (newManagedState.equals(Managed.ManagedState.Unmanaged)) {
 +                boolean success = false;
 +                try {
 +                    
cluster.setManagedState(Managed.ManagedState.PrepareUnmanaged);
 +                    _clusterDao.update(cluster.getId(), cluster);
 +                    List<HostVO> hosts = listAllHosts(Host.Type.Routing, 
cluster.getId(), cluster.getPodId(), cluster.getDataCenterId());
 +                    for (final 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)) {
 +                            final String msg = "host " + 
host.getPrivateIpAddress() + " should not be in " + host.getStatus().toString() 
+ " status";
 +                            throw new CloudRuntimeException("PrepareUnmanaged 
Failed due to " + msg);
 +                        }
 +                    }
 +
 +                    for (final HostVO host : hosts) {
 +                        if (host.getStatus().equals(Status.Up)) {
 +                            umanageHost(host.getId());
 +                        }
 +                    }
 +                    final int retry = 40;
 +                    boolean lsuccess = true;
 +                    for (int i = 0; i < retry; i++) {
 +                        lsuccess = true;
 +                        try {
 +                            Thread.sleep(5 * 1000);
 +                        } catch (final Exception e) {
 +                        }
 +                        hosts = listAllUpAndEnabledHosts(Host.Type.Routing, 
cluster.getId(), cluster.getPodId(), cluster.getDataCenterId());
 +                        for (final 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 {
 +                    cluster.setManagedState(success ? 
Managed.ManagedState.Unmanaged : Managed.ManagedState.PrepareUnmanagedError);
 +                    _clusterDao.update(cluster.getId(), cluster);
 +                }
 +            } else if (newManagedState.equals(Managed.ManagedState.Managed)) {
 +                cluster.setManagedState(Managed.ManagedState.Managed);
 +                _clusterDao.update(cluster.getId(), cluster);
 +            }
 +
 +        }
 +
 +        return cluster;
 +    }
 +
 +    @Override
 +    public Host cancelMaintenance(final CancelMaintenanceCmd cmd) {
 +        final Long hostId = cmd.getId();
 +
 +        // verify input parameters
 +        final 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);
 +        final 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) throws 
AgentUnavailableException {
 +        Long hostId = cmd.getId();
 +
 +        HostVO host = _hostDao.findById(hostId);
 +        if (host == null) {
 +            throw new InvalidParameterValueException("Host with id " + 
hostId.toString() + " doesn't exist");
 +        }
 +        _agentMgr.reconnect(hostId);
 +        return host;
 +    }
 +
 +    @Override
 +    public boolean resourceStateTransitTo(final Host host, final 
ResourceState.Event event, final long msId) throws NoTransitionException {
 +        final ResourceState currentState = host.getResourceState();
 +        final 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){
 +            final CapacityState capacityState =  nextState == 
ResourceState.Enabled ? CapacityState.Enabled : CapacityState.Disabled;
 +            final short[] capacityTypes = {Capacity.CAPACITY_TYPE_CPU, 
Capacity.CAPACITY_TYPE_MEMORY};
 +            _capacityDao.updateCapacityState(null, null, null, host.getId(), 
capacityState.toString(), capacityTypes);
 +
 +            final StoragePoolVO storagePool = 
_storageMgr.findLocalStorageOnHost(host.getId());
 +
 +            if(storagePool != null){
 +                final short[] capacityTypesLocalStorage = 
{Capacity.CAPACITY_TYPE_LOCAL_STORAGE};
 +                _capacityDao.updateCapacityState(null, null, null, 
storagePool.getId(), capacityState.toString(), capacityTypesLocalStorage);
 +            }
 +        }
 +        return _hostDao.updateResourceState(currentState, event, nextState, 
host);
 +    }
 +
 +    private boolean doMaintain(final long hostId) {
 +        final HostVO host = _hostDao.findById(hostId);
 +        final MaintainAnswer answer = 
(MaintainAnswer)_agentMgr.easySend(hostId, new MaintainCommand());
 +        if (answer == null || !answer.getResult()) {
 +            s_logger.warn("Unable to send MaintainCommand to host: " + 
hostId);
 +            return false;
 +        }
 +
 +        try {
 +            resourceStateTransitTo(host, 
ResourceState.Event.AdminAskMaintenace, _nodeId);
 +        } catch (final NoTransitionException e) {
 +            final String err = "Cannot transmit resource state of host " + 
host.getId() + " to " + ResourceState.Maintenance;
 +            s_logger.debug(err, e);
 +            throw new CloudRuntimeException(err + e.getMessage());
 +        }
 +
 +        
ActionEventUtils.onStartedActionEvent(CallContext.current().getCallingUserId(), 
CallContext.current().getCallingAccountId(), 
EventTypes.EVENT_MAINTENANCE_PREPARE, "starting maintenance for host " + 
hostId, true, 0);
 +        _agentMgr.pullAgentToMaintenance(hostId);
++        setHostMaintenanceRetries(host);
 +
 +        /* TODO: move below to listener */
 +        if (host.getType() == Host.Type.Routing) {
 +
 +            final List<VMInstanceVO> vms = _vmDao.listByHostId(hostId);
 +            if (vms.size() == 0) {
 +                return true;
 +            }
 +
 +            final 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()
 +                        || 
_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), 
GPU.Keys.vgpuType.toString()) != null) {
 +                    // Migration is not supported for VGPU Vms so stop them.
 +                    // for the last host in this cluster, stop all the VMs
 +                    _haMgr.scheduleStop(vm, hostId, WorkType.ForceStop);
 +                } else if 
(HypervisorType.LXC.equals(host.getHypervisorType()) && 
VirtualMachine.Type.User.equals(vm.getType())){
 +                    //Migration is not supported for LXC Vms. Schedule 
restart instead.
 +                    _haMgr.scheduleRestart(vm, false);
 +                } else {
 +                    _haMgr.scheduleMigration(vm);
 +                }
 +            }
 +        }
 +        return true;
 +    }
 +
++    /**
++     * Set retries for transiting the host into Maintenance
++     */
++    protected void setHostMaintenanceRetries(HostVO host) {
++        Integer retries = HostMaintenanceRetries.valueIn(host.getClusterId());
++        retryHostMaintenance.put(host.getId(), retries);
++        s_logger.debug(String.format("Setting the host %s (%s) retries for 
Maintenance mode: %s",
++                host.getId(), host.getName(), retries));
++    }
++
 +    @Override
 +    public boolean maintain(final long hostId) throws 
AgentUnavailableException {
 +        final Boolean result = propagateResourceEvent(hostId, 
ResourceState.Event.AdminAskMaintenace);
 +        if (result != null) {
 +            return result;
 +        }
 +
 +        return doMaintain(hostId);
 +    }
 +
 +    @Override
 +    public Host maintain(final PrepareForMaintenanceCmd cmd) {
 +        final Long hostId = cmd.getId();
 +        final 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.getId())) {
 +            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 (final AgentUnavailableException e) {
 +            throw new CloudRuntimeException("Unable to prepare for 
maintenance host " + hostId);
 +        }
 +    }
 +
 +    /**
 +     * Add VNC details as user VM details for each VM in 'vms' (KVM hosts 
only)
 +     */
 +    protected void setKVMVncAccess(long hostId, List<VMInstanceVO> vms) {
 +        for (VMInstanceVO vm : vms) {
 +            GetVncPortAnswer vmVncPortAnswer = (GetVncPortAnswer) 
_agentMgr.easySend(hostId, new GetVncPortCommand(vm.getId(), 
vm.getInstanceName()));
 +            if (vmVncPortAnswer != null) {
 +                userVmDetailsDao.addDetail(vm.getId(), "kvm.vnc.address", 
vmVncPortAnswer.getAddress(), true);
 +                userVmDetailsDao.addDetail(vm.getId(), "kvm.vnc.port", 
String.valueOf(vmVncPortAnswer.getPort()), true);
 +            }
 +        }
 +    }
 +
 +    /**
 +     * Configure VNC access for host VMs which have failed migrating to 
another host while trying to enter Maintenance mode
 +     */
 +    protected void configureVncAccessForKVMHostFailedMigrations(HostVO host, 
List<VMInstanceVO> failedMigrations) {
 +        if (host.getHypervisorType().equals(HypervisorType.KVM)) {
 +            _agentMgr.pullAgentOutMaintenance(host.getId());
 +            setKVMVncAccess(host.getId(), failedMigrations);
 +            _agentMgr.pullAgentToMaintenance(host.getId());
 +        }
 +    }
 +
 +    /**
 +     * Set host into ErrorInMaintenance state, as errors occurred during VM 
migrations. Do the following:
 +     * - Cancel scheduled migrations for those which have already failed
 +     * - Configure VNC access for VMs (KVM hosts only)
 +     */
 +    protected boolean setHostIntoErrorInMaintenance(HostVO host, 
List<VMInstanceVO> failedMigrations) throws NoTransitionException {
 +        s_logger.debug("Unable to migrate " + failedMigrations.size() + " 
VM(s) from host " + host.getUuid());
 +        _haMgr.cancelScheduledMigrations(host);
 +        configureVncAccessForKVMHostFailedMigrations(host, failedMigrations);
 +        resourceStateTransitTo(host, ResourceState.Event.UnableToMigrate, 
_nodeId);
 +        return false;
 +    }
 +
 +    /**
 +     * Safely transit host into Maintenance mode
 +     */
 +    protected boolean setHostIntoMaintenance(HostVO host) throws 
NoTransitionException {
 +        s_logger.debug("Host " + host.getUuid() + " entering in Maintenance");
 +        resourceStateTransitTo(host, 
ResourceState.Event.InternalEnterMaintenance, _nodeId);
 +        
ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(),
 CallContext.current().getCallingAccountId(),
 +                EventVO.LEVEL_INFO, EventTypes.EVENT_MAINTENANCE_PREPARE,
 +                "completed maintenance for host " + host.getId(), 0);
 +        return true;
 +    }
 +
 +    /**
 +     * Return true if host goes into Maintenance mode, only when:
 +     * - No Running, Migrating or Failed migrations (host_id = last_host_id) 
for the host
 +     */
 +    protected boolean isHostInMaintenance(HostVO host, List<VMInstanceVO> 
runningVms, List<VMInstanceVO> migratingVms, List<VMInstanceVO> 
failedMigrations) throws NoTransitionException {
 +        if (CollectionUtils.isEmpty(runningVms) && 
CollectionUtils.isEmpty(migratingVms)) {
 +            return CollectionUtils.isEmpty(failedMigrations) ?
 +                    setHostIntoMaintenance(host) :
 +                    setHostIntoErrorInMaintenance(host, failedMigrations);
++        } else if (retryHostMaintenance.containsKey(host.getId())) {
++            Integer retriesLeft = retryHostMaintenance.get(host.getId());
++            if (retriesLeft != null) {
++                if (retriesLeft <= 0) {
++                    retryHostMaintenance.remove(host.getId());
++                    s_logger.debug(String.format("No retries left while 
preparing KVM host %s (%s) for Maintenance, " +
++                                    "please investigate this connection.",
++                            host.getId(), host.getName()));
++                    return setHostIntoErrorInMaintenance(host, 
failedMigrations);
++                }
++                retriesLeft--;
++                retryHostMaintenance.put(host.getId(), retriesLeft);
++                s_logger.debug(String.format("Retries left preparing KVM host 
%s (%s) for Maintenance: %s",
++                        host.getId(), host.getName(), retriesLeft));
++            }
 +        }
++
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean checkAndMaintain(final long hostId) {
 +        boolean hostInMaintenance = false;
 +        final HostVO host = _hostDao.findById(hostId);
 +
 +        try {
 +            if (host.getType() != Host.Type.Storage) {
 +                final List<VMInstanceVO> vos = _vmDao.listByHostId(hostId);
 +                final List<VMInstanceVO> vosMigrating = 
_vmDao.listVmsMigratingFromHost(hostId);
 +                final List<VMInstanceVO> failedVmMigrations = 
_vmDao.listNonMigratingVmsByHostEqualsLastHost(hostId);
 +
 +                hostInMaintenance = isHostInMaintenance(host, vos, 
vosMigrating, failedVmMigrations);
 +            }
 +        } catch (final NoTransitionException e) {
 +            s_logger.debug("Cannot transmit host " + host.getId() + "to 
Maintenance state", e);
 +        }
 +        return hostInMaintenance;
 +    }
 +
 +    @Override
 +    public Host updateHost(final UpdateHostCmd cmd) throws 
NoTransitionException {
 +        final Long hostId = cmd.getId();
 +        final Long guestOSCategoryId = cmd.getOsCategoryId();
 +
 +        // Verify that the host exists
 +        final HostVO host = _hostDao.findById(hostId);
 +        if (host == null) {
 +            throw new InvalidParameterValueException("Host with id " + hostId 
+ " doesn't exist");
 +        }
 +
 +        if (cmd.getAllocationState() != null) {
 +            final 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) || 
_guestOSCategoryDao.findById(guestOSCategoryId) == null) {
 +                throw new InvalidParameterValueException("Please specify a 
valid guest OS category.");
 +            }
 +
 +            final GuestOSCategoryVO guestOSCategory = 
_guestOSCategoryDao.findById(guestOSCategoryId);
 +            final DetailVO guestOSDetail = _hostDetailsDao.findDetail(hostId, 
"guest.os.category.id");
 +
 +            if (guestOSCategory != null && 
!GuestOSCategoryVO.CATEGORY_NONE.equalsIgnoreCase(guestOSCategory.getName())) {
 +                // Create/Update an entry for guest.os.category.id
 +                if (guestOSDetail != null) {
 +                    
guestOSDetail.setValue(String.valueOf(guestOSCategory.getId()));
 +                    _hostDetailsDao.update(guestOSDetail.getId(), 
guestOSDetail);
 +                } else {
 +                    final Map<String, String> detail = new HashMap<String, 
String>();
 +                    detail.put("guest.os.category.id", 
String.valueOf(guestOSCategory.getId()));
 +                    _hostDetailsDao.persist(hostId, detail);
 +                }
 +            } else {
 +                // Delete any existing entry for guest.os.category.id
 +                if (guestOSDetail != null) {
 +                    _hostDetailsDao.remove(guestOSDetail.getId());
 +                }
 +            }
 +        }
 +
 +        final List<String> hostTags = cmd.getHostTags();
 +        if (hostTags != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Updating Host Tags to :" + hostTags);
 +            }
 +            _hostTagsDao.persist(hostId, hostTags);
 +        }
 +
 +        final String url = cmd.getUrl();
 +        if (url != null) {
 +            _storageMgr.updateSecondaryStorage(cmd.getId(), cmd.getUrl());
 +        }
 +
 +        final HostVO updatedHost = _hostDao.findById(hostId);
 +        return updatedHost;
 +    }
 +
 +    @Override
 +    public Cluster getCluster(final Long clusterId) {
 +        return _clusterDao.findById(clusterId);
 +    }
 +
 +    @Override
 +    public DataCenter getZone(Long zoneId) {
 +        return _dcDao.findById(zoneId);
 +    }
 +
 +    @Override
 +    public boolean configure(final String name, final Map<String, Object> 
params) throws ConfigurationException {
 +        _defaultSystemVMHypervisor = 
HypervisorType.getType(_configDao.getValue(Config.SystemVMDefaultHypervisor.toString()));
 +        _gson = GsonHelper.getGson();
 +
 +        _hypervisorsInDC = _hostDao.createSearchBuilder(String.class);
 +        _hypervisorsInDC.select(null, Func.DISTINCT, 
_hypervisorsInDC.entity().getHypervisorType());
 +        _hypervisorsInDC.and("hypervisorType", 
_hypervisorsInDC.entity().getHypervisorType(), SearchCriteria.Op.NNULL);
 +        _hypervisorsInDC.and("dataCenter", 
_hypervisorsInDC.entity().getDataCenterId(), SearchCriteria.Op.EQ);
 +        _hypervisorsInDC.and("id", _hypervisorsInDC.entity().getId(), 
SearchCriteria.Op.NEQ);
 +        _hypervisorsInDC.and("type", _hypervisorsInDC.entity().getType(), 
SearchCriteria.Op.EQ);
 +        _hypervisorsInDC.done();
 +
 +        _gpuAvailability = _hostGpuGroupsDao.createSearchBuilder();
 +        _gpuAvailability.and("hostId", _gpuAvailability.entity().getHostId(), 
Op.EQ);
 +        _gpuAvailability.and("groupName", 
_gpuAvailability.entity().getGroupName(), Op.EQ);
 +        final SearchBuilder<VGPUTypesVO> join1 = 
_vgpuTypesDao.createSearchBuilder();
 +        join1.and("vgpuType", join1.entity().getVgpuType(), Op.EQ);
 +        join1.and("remainingCapacity", join1.entity().getRemainingCapacity(), 
Op.GT);
 +        _gpuAvailability.join("groupId", join1, 
_gpuAvailability.entity().getId(), join1.entity().getGpuGroupId(), 
JoinBuilder.JoinType.INNER);
 +        _gpuAvailability.done();
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public List<HypervisorType> getSupportedHypervisorTypes(final long 
zoneId, final boolean forVirtualRouter, final Long podId) {
 +        final 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 (final ClusterVO cluster : clustersForZone) {
 +            final HypervisorType hType = cluster.getHypervisorType();
 +            if (!forVirtualRouter || forVirtualRouter && hType != 
HypervisorType.BareMetal && hType != HypervisorType.Ovm) {
 +                hypervisorTypes.add(hType);
 +            }
 +        }
 +
 +        return hypervisorTypes;
 +    }
 +
 +    @Override
 +    public HypervisorType getDefaultHypervisor(final long zoneId) {
 +        HypervisorType defaultHyper = HypervisorType.None;
 +        if (_defaultSystemVMHypervisor != HypervisorType.None) {
 +            defaultHyper = _defaultSystemVMHypervisor;
 +        }
 +
 +        final DataCenterVO dc = _dcDao.findById(zoneId);
 +        if (dc == null) {
 +            return HypervisorType.None;
 +        }
 +        _dcDao.loadDetails(dc);
 +        final String defaultHypervisorInZone = 
dc.getDetail("defaultSystemVMHypervisorType");
 +        if (defaultHypervisorInZone != null) {
 +            defaultHyper = HypervisorType.getType(defaultHypervisorInZone);
 +        }
 +
 +        final List<VMTemplateVO> systemTemplates = 
_templateDao.listAllSystemVMTemplates();
 +        boolean isValid = false;
 +        for (final VMTemplateVO template : systemTemplates) {
 +            if (template.getHypervisorType() == defaultHyper) {
 +                isValid = true;
 +                break;
 +            }
 +        }
 +
 +        if (isValid) {
 +            final 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(final long zoneId) {
 +        HypervisorType defaultHype = getDefaultHypervisor(zoneId);
 +        if (defaultHype == HypervisorType.None) {
 +            final List<HypervisorType> supportedHypes = 
getSupportedHypervisorTypes(zoneId, false, null);
 +            if (supportedHypes.size() > 0) {
 +                Collections.shuffle(supportedHypes);
 +                defaultHype = supportedHypes.get(0);
 +            }
 +        }
 +
 +        if (defaultHype == HypervisorType.None) {
 +            defaultHype = HypervisorType.Any;
 +        }
 +        return defaultHype;
 +    }
 +
 +    @Override
 +    public void registerResourceStateAdapter(final String name, final 
ResourceStateAdapter adapter) {
 +        synchronized (_resourceStateAdapters) {
 +            if (_resourceStateAdapters.get(name) != null) {
 +                throw new CloudRuntimeException(name + " has registered");
 +            }
 +            _resourceStateAdapters.put(name, adapter);
 +        }
 +    }
 +
 +    @Override
 +    public void unregisterResourceStateAdapter(final String name) {
 +        synchronized (_resourceStateAdapters) {
 +            _resourceStateAdapters.remove(name);
 +        }
 +    }
 +
 +    private Object dispatchToStateAdapters(final ResourceStateAdapter.Event 
event, final boolean singleTaker, final Object... args) {
 +        synchronized (_resourceStateAdapters) {
 +            final Iterator<Map.Entry<String, ResourceStateAdapter>> it = 
_resourceStateAdapters.entrySet().iterator();
 +            Object result = null;
 +            while (it.hasNext()) {
 +                final Map.Entry<String, ResourceStateAdapter> item = 
it.next();
 +                final ResourceStateAdapter adapter = item.getValue();
 +
 +                final String msg = "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 (final 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(final HostPodVO pod, final DataCenterVO dc, final 
String serverPrivateIP, final String serverPrivateNetmask) throws 
IllegalArgumentException {
 +        if (serverPrivateIP == null) {
 +            return;
 +        }
 +        // Get the CIDR address and CIDR size
 +        final String cidrAddress = pod.getCidrAddress();
 +        final long cidrSize = pod.getCidrSize();
 +
 +        // If the server's private IP address is not in the same subnet as the
 +        // pod's CIDR, return false
 +        final String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, 
cidrSize);
 +        final 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
 +        final String cidrNetmask = NetUtils.getCidrSubNet("255.255.255.255", 
cidrSize);
 +        final long cidrNetmaskNumeric = NetUtils.ip2Long(cidrNetmask);
 +        final 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(final HostPodVO pod, final String 
serverPrivateIP, final String serverPrivateNetmask) {
 +        if (serverPrivateIP == null) {
 +            return true;
 +        }
 +        // Get the CIDR address and CIDR size
 +        final String cidrAddress = pod.getCidrAddress();
 +        final long cidrSize = pod.getCidrSize();
 +
 +        // If the server's private IP address is not in the same subnet as the
 +        // pod's CIDR, return false
 +        final String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, 
cidrSize);
 +        final 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
 +        final String cidrNetmask = NetUtils.getCidrSubNet("255.255.255.255", 
cidrSize);
 +        final long cidrNetmaskNumeric = NetUtils.ip2Long(cidrNetmask);
 +        final long serverNetmaskNumeric = 
NetUtils.ip2Long(serverPrivateNetmask);
 +        if (serverNetmaskNumeric > cidrNetmaskNumeric) {
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    private HostVO getNewHost(StartupCommand[] startupCommands) {
 +        StartupCommand startupCommand = startupCommands[0];
 +
 +        HostVO host = findHostByGuid(startupCommand.getGuid());
 +
 +        if (host != null) {
 +            return host;
 +        }
 +
 +        host = findHostByGuid(startupCommand.getGuidWithoutResource());
 +
 +        if (host != null) {
 +            return host;
 +        }
 +
 +        return null;
 +    }
 +
 +    protected HostVO createHostVO(final StartupCommand[] cmds, final 
ServerResource resource, final Map<String, String> details, List<String> 
hostTags,
 +            final ResourceStateAdapter.Event stateEvent) {
 +        boolean newHost = false;
 +        StartupCommand startup = cmds[0];
 +
 +        HostVO host = getNewHost(cmds);
 +
 +        if (host == null) {
 +            host = new HostVO(startup.getGuid());
 +
 +            newHost = true;
 +        }
 +
 +        String dataCenter = startup.getDataCenter();
 +        String pod = startup.getPod();
 +        final String cluster = startup.getCluster();
 +
 +        if (pod != null && dataCenter != null && 
pod.equalsIgnoreCase("default") && dataCenter.equalsIgnoreCase("default")) {
 +            final List<HostPodVO> pods = _podDao.listAllIncludingRemoved();
 +            for (final 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) {
 +                s_logger.debug("Cannot parse " + dataCenter + " into Long.");
 +            }
 +        }
 +        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) {
 +                s_logger.debug("Cannot parse " + pod + " into Long.");
 +            }
 +        }
 +        /*
 +         * 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
 +         */
 +        final Long podId = p == null ? null : p.getId();
 +
 +        Long clusterId = null;
 +        if (cluster != null) {
 +            try {
 +                clusterId = Long.valueOf(cluster);
 +            } catch (final NumberFormatException e) {
 +                if (podId != null) {
 +                    ClusterVO c = _clusterDao.findBy(cluster, 
podId.longValue());
 +                    if (c == null) {
 +                        c = new ClusterVO(dcId, podId.longValue(), cluster);
 +                        c = _clusterDao.persist(c);
 +                    }
 +                    clusterId = c.getId();
 +                }
 +            }
 +        }
 +
 +        if (startup instanceof StartupRoutingCommand) {
 +            final StartupRoutingCommand ssCmd = 
(StartupRoutingCommand)startup;
 +            final List<String> implicitHostTags = ssCmd.getHostTags();
 +            if (!implicitHostTags.isEmpty()) {
 +                if (hostTags == null) {
 +                    hostTags = _hostTagsDao.gethostTags(host.getId());
 +                }
 +                if (hostTags != null) {
 +                    implicitHostTags.removeAll(hostTags);
 +                    hostTags.addAll(implicitHostTags);
 +                } else {
 +                    hostTags = implicitHostTags;
 +                }
 +            }
 +        }
 +
 +        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 (newHost) {
 +            host = _hostDao.persist(host);
 +        } else {
 +            _hostDao.update(host.getId(), host);
 +        }
 +
 +        if (startup instanceof StartupRoutingCommand) {
 +            final StartupRoutingCommand ssCmd = 
(StartupRoutingCommand)startup;
 +
 +            updateSupportsClonedVolumes(host, 
ssCmd.getSupportsClonedVolumes());
 +        }
 +
 +        try {
 +            resourceStateTransitTo(host, ResourceState.Event.InternalCreated, 
_nodeId);
 +            /* Agent goes to Connecting status */
 +            _agentMgr.agentStatusTransitTo(host, Status.Event.AgentConnected, 
_nodeId);
 +        } catch (final 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 (final NoTransitionException e1) {
 +                s_logger.debug("Cannot transmit host " + host.getId() + "to 
Error state", e);
 +            }
 +        }
 +
 +        return host;
 +    }
 +
 +    private void updateSupportsClonedVolumes(HostVO host, boolean 
supportsClonedVolumes) {
 +        final String name = "supportsResign";
 +
 +        DetailVO hostDetail = _hostDetailsDao.findDetail(host.getId(), name);
 +
 +        if (hostDetail != null) {
 +            if (supportsClonedVolumes) {
 +                hostDetail.setValue(Boolean.TRUE.toString());
 +
 +                _hostDetailsDao.update(hostDetail.getId(), hostDetail);
 +            }
 +            else {
 +                _hostDetailsDao.remove(hostDetail.getId());
 +            }
 +        }
 +        else {
 +            if (supportsClonedVolumes) {
 +                hostDetail = new DetailVO(host.getId(), name, 
Boolean.TRUE.toString());
 +
 +                _hostDetailsDao.persist(hostDetail);
 +            }
 +        }
 +
 +        boolean clusterSupportsResigning = true;
 +
 +        List<HostVO> hostVOs = _hostDao.findByClusterId(host.getClusterId());
 +
 +        for (HostVO hostVO : hostVOs) {
 +            DetailVO hostDetailVO = 
_hostDetailsDao.findDetail(hostVO.getId(), name);
 +
 +            if (hostDetailVO == null || 
Boolean.parseBoolean(hostDetailVO.getValue()) == false) {
 +                clusterSupportsResigning = false;
 +
 +                break;
 +            }
 +        }
 +
 +        ClusterDetailsVO clusterDetailsVO = 
_clusterDetailsDao.findDetail(host.getClusterId(), name);
 +
 +        if (clusterDetailsVO != null) {
 +            if (clusterSupportsResigning) {
 +                clusterDetailsVO.setValue(Boolean.TRUE.toString());
 +
 +                _clusterDetailsDao.update(clusterDetailsVO.getId(), 
clusterDetailsVO);
 +            }
 +            else {
 +                _clusterDetailsDao.remove(clusterDetailsVO.getId());
 +            }
 +        }
 +        else {
 +            if (clusterSupportsResigning) {
 +                clusterDetailsVO = new ClusterDetailsVO(host.getClusterId(), 
name, Boolean.TRUE.toString());
 +
 +                _clusterDetailsDao.persist(clusterDetailsVO);
 +            }
 +        }
 +    }
 +
 +    private boolean isFirstHostInCluster(final HostVO host) {
 +        boolean isFirstHost = true;
 +        if (host.getClusterId() != null) {
 +            final SearchBuilder<HostVO> sb = _hostDao.createSearchBuilder();
 +            sb.and("removed", sb.entity().getRemoved(), 
SearchCriteria.Op.NULL);
 +            sb.and("cluster", sb.entity().getClusterId(), 
SearchCriteria.Op.EQ);
 +            sb.done();
 +            final SearchCriteria<HostVO> sc = sb.create();
 +            sc.setParameters("cluster", host.getClusterId());
 +
 +            final List<HostVO> hosts = _hostDao.search(sc, null);
 +            if (hosts != null && hosts.size() > 1) {
 +                isFirstHost = false;
 +            }
 +        }
 +        return isFirstHost;
 +    }
 +
 +    private void markHostAsDisconnected(HostVO host, final StartupCommand[] 
cmds) {
 +        if (host == null) { // in case host is null due to some errors, try
 +            // reloading the host from db
 +            if (cmds != null) {
 +                final StartupCommand firstCmd = cmds[0];
 +                host = findHostByGuid(firstCmd.getGuid());
 +                if (host == null) {
 +                    host = findHostByGuid(firstCmd.getGuidWithoutResource());
 +                }
 +            }
 +        }
 +
 +        if (host != null) {
 +            // Change agent status to Alert, so that host is considered for
 +            // reconnection next time
 +            _agentMgr.agentStatusTransitTo(host, 
Status.Event.AgentDisconnected, _nodeId);
 +        }
 +    }
 +
 +    private Host createHostAndAgent(final ServerResource resource, final 
Map<String, String> details, final boolean old, final List<String> hostTags, 
final boolean forRebalance) {
 +        HostVO host = null;
 +        StartupCommand[] cmds = null;
 +        boolean hostExists = false;
 +        boolean created = false;
 +
 +        try {
 +            cmds = resource.initialize();
 +            if (cmds == null) {
 +                s_logger.info("Unable to fully initialize the agent because 
no StartupCommands are returned");
 +                return null;
 +            }
 +
 +            /* Generate a random version in a dev setup situation */
 +            if (this.getClass().getPackage().getImplementationVersion() == 
null) {
 +                for (final StartupCommand cmd : cmds) {
 +                    if (cmd.getVersion() == null) {
 +                        
cmd.setVersion(Long.toString(System.currentTimeMillis()));
 +                    }
 +                }
 +            }
 +
 +            if (s_logger.isDebugEnabled()) {
 +                new Request(-1l, -1l, cmds, true, false).logD("Startup 
request from directly connected host: ", true);
 +            }
 +
 +            if (old) {
 +                final StartupCommand firstCmd = cmds[0];
 +                host = findHostByGuid(firstCmd.getGuid());
 +                if (host == null) {
 +                    host = findHostByGuid(firstCmd.getGuidWithoutResource());
 +                }
 +                if (host != null && host.getRemoved() == null) { // host 
already added, no need to add again
 +                    s_logger.debug("Found the host " + host.getId() + " by 
guid: " + firstCmd.getGuid() + ", old host reconnected as new");
 +                    hostExists = true; // ensures that host status is left 
unchanged in case of adding same one again
 +                    return null;
 +                }
 +            }
 +
 +            // find out if the host we want to connect to is new (so we can 
send an event)
 +            boolean newHost = getNewHost(cmds) == null;
 +
 +            host = createHostVO(cmds, resource, details, hostTags, 
ResourceStateAdapter.Event.CREATE_HOST_VO_FOR_DIRECT_CONNECT);
 +
 +            if (host != null) {
 +                created = _agentMgr.handleDirectConnectAgent(host, cmds, 
resource, forRebalance, newHost);
 +                /* reload myself from database */
 +                host = _hostDao.findById(host.getId());
 +            }
 +        } catch (final Exception e) {
 +            s_logger.warn("Unable to connect due to ", e);
 +        } finally {
 +            if (hostExists) {
 +                if (cmds != null) {
 +                    resource.disconnected();
 +                }
 +            } else {
 +                if (!created) {
 +                    if (cmds != null) {
 +                        resource.disconnected();
 +                    }
 +                    markHostAsDisconnected(host, cmds);
 +                }
 +            }
 +        }
 +
 +        return host;
 +    }
 +
 +    private Host createHostAndAgentDeferred(final ServerResource resource, 
final Map<String, String> details, final boolean old, final List<String> 
hostTags, final boolean forRebalance) {
 +        HostVO host = null;
 +        StartupCommand[] cmds = null;
 +        boolean hostExists = false;
 +        boolean deferAgentCreation = true;
 +        boolean created = false;
 +
 +        try {
 +            cmds = resource.initialize();
 +            if (cmds == null) {
 +                s_logger.info("Unable to fully initialize the agent because 
no StartupCommands are returned");
 +                return null;
 +            }
 +
 +            /* Generate a random version in a dev setup situation */
 +            if (this.getClass().getPackage().getImplementationVersion() == 
null) {
 +                for (final StartupCommand cmd : cmds) {
 +                    if (cmd.getVersion() == null) {
 +                        
cmd.setVersion(Long.toString(System.currentTimeMillis()));
 +                    }
 +                }
 +            }
 +
 +            if (s_logger.isDebugEnabled()) {
 +                new Request(-1l, -1l, cmds, true, false).logD("Startup 
request from directly connected host: ", true);
 +            }
 +
 +            if (old) {
 +                final StartupCommand firstCmd = cmds[0];
 +                host = findHostByGuid(firstCmd.getGuid());
 +                if (host == null) {
 +                    host = findHostByGuid(firstCmd.getGuidWithoutResource());
 +                }
 +                if (host != null && host.getRemoved() == null) { // host 
already
 +                    // added, no
 +                    // need to add
 +                    // again
 +                    s_logger.debug("Found the host " + host.getId() + " by 
guid: " + firstCmd.getGuid() + ", old host reconnected as new");
 +                    hostExists = true; // ensures that host status is left
 +                    // unchanged in case of adding same one
 +                    // again
 +                    return null;
 +                }
 +            }
 +
 +            host = null;
 +            boolean newHost = false;
 +
 +            final GlobalLock addHostLock = 
GlobalLock.getInternLock("AddHostLock");
 +
 +            try {
 +                if 
(addHostLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) {
 +                    // to safely determine first host in cluster in multi-MS 
scenario
 +                    try {
 +                        // find out if the host we want to connect to is new 
(so we can send an event)
 +                        newHost = getNewHost(cmds) == null;
 +
 +                        host = createHostVO(cmds, resource, details, 
hostTags, ResourceStateAdapter.Event.CREATE_HOST_VO_FOR_DIRECT_CONNECT);
 +
 +                        if (host != null) {
 +                            // if first host in cluster no need to defer 
agent creation
 +                            deferAgentCreation = !isFirstHostInCluster(host);
 +                        }
 +                    } finally {
 +                        addHostLock.unlock();
 +                    }
 +                }
 +            } finally {
 +                addHostLock.releaseRef();
 +            }
 +
 +            if (host != null) {
 +                if (!deferAgentCreation) { // if first host in cluster then
 +                    created = _agentMgr.handleDirectConnectAgent(host, cmds, 
resource, forRebalance, newHost);
 +                    host = _hostDao.findById(host.getId()); // reload
 +                } else {
 +                    host = _hostDao.findById(host.getId()); // reload
 +                    // force host status to 'Alert' so that it is loaded for
 +                    // connection during next scan task
 +                    _agentMgr.agentStatusTransitTo(host, 
Status.Event.AgentDisconnected, _nodeId);
 +
 +                    host = _hostDao.findById(host.getId()); // reload
 +                    host.setLastPinged(0); // so that scan task can pick it up
 +                    _hostDao.update(host.getId(), host);
 +
 +                }
 +            }
 +        } catch (final Exception e) {
 +            s_logger.warn("Unable to connect due to ", e);
 +        } finally {
 +            if (hostExists) {
 +                if (cmds != null) {
 +                    resource.disconnected();
 +                }
 +            } else {
 +                if (!deferAgentCreation && !created) {
 +                    if (cmds != null) {
 +                        resource.disconnected();
 +                    }
 +                    markHostAsDisconnected(host, cmds);
 +                }
 +            }
 +        }
 +
 +        return host;
 +    }
 +
 +    @Override
 +    public Host createHostAndAgent(final Long hostId, final ServerResource 
resource, final Map<String, String> details, final boolean old, final 
List<String> hostTags, final boolean forRebalance) {
 +        final Host host = createHostAndAgent(resource, details, old, 
hostTags, forRebalance);
 +        return host;
 +    }
 +
 +    @Override
 +    public Host addHost(final long zoneId, final ServerResource resource, 
final Type hostType, final 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);
 +        }
 +
 +        final Map<String, String> details = hostDetails;
 +        final String guid = details.get("guid");
 +        final List<HostVO> currentHosts = 
listAllUpAndEnabledHostsInOneZoneByType(hostType, zoneId);
 +        for (final HostVO currentHost : currentHosts) {
 +            if (currentHost.getGuid().equals(guid)) {
 +                return currentHost;
 +            }
 +        }
 +
 +        return createHostAndAgent(resource, hostDetails, true, null, false);
 +    }
 +
 +    @Override
 +    public HostVO createHostVOForConnectedAgent(final StartupCommand[] cmds) {
 +        return createHostVO(cmds, null, null, null, 
ResourceStateAdapter.Event.CREATE_HOST_VO_FOR_CONNECTED);
 +    }
 +
 +    private void checkIPConflicts(final HostPodVO pod, final DataCenterVO dc, 
final String serverPrivateIP, final String serverPrivateNetmask, final String 
serverPublicIP, final 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 (!ObjectUtils.equals(serverPrivateIP, serverPublicIP)) {
 +            if (!_privateIPAddressDao.mark(dc.getId(), pod.getId(), 
serverPrivateIP)) {
 +                // If the server's private IP address is already in the
 +                // database, return false
 +                final 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) {
 +                    final 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
 +            final 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(final HostVO host, final 
StartupRoutingCommand ssCmd, final HypervisorType hyType, Map<String, String> 
details, final 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");
 +        }
 +
 +        final 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;
 +            }
 +        }
 +
 +        final HostPodVO pod = _podDao.findById(host.getPodId());
 +        final 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.setCpuSockets(ssCmd.getCpuSockets());
 +        host.setCpus(ssCmd.getCpus());
 +        host.setTotalMemory(ssCmd.getMemory());
 +        host.setSpeed(ssCmd.getSpeed());
 +        host.setHypervisorType(hyType);
 +        host.setHypervisorVersion(ssCmd.getHypervisorVersion());
 +        host.setGpuGroups(ssCmd.getGpuGroupDetails());
 +        return host;
 +    }
 +
 +    @Override
 +    public void deleteRoutingHost(final HostVO host, final boolean isForced, 
final 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());
 +        }
 +
 +        if (forceDestroyStorage) {
 +            // put local storage into mainenance mode, will set all the VMs on
 +            // this local storage into stopped state
 +            final StoragePoolVO storagePool = 
_storageMgr.findLocalStorageOnHost(host.getId());
 +            if (storagePool != null) {
 +                if (storagePool.getStatus() == StoragePoolStatus.Up || 
storagePool.getStatus() == StoragePoolStatus.ErrorInMaintenance) {
 +                    try {
 +                        final StoragePool pool = 
_storageSvr.preparePrimaryStorageForMaintenance(storagePool.getId());
 +                        if (pool == null) {
 +                            s_logger.debug("Failed to set primary storage 
into maintenance mode");
 +
 +                            throw new UnableDeleteHostException("Failed to 
set primary storage into maintenance mode");
 +                        }
 +                    } catch (final 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());
 +                    }
 +                }
 +
 +                final List<VMInstanceVO> vmsOnLocalStorage = 
_storageMgr.listByStoragePool(storagePool.getId());
 +                for (final VMInstanceVO vm : vmsOnLocalStorage) {
 +                    try {
 +                        _vmMgr.destroy(vm.getUuid(), false);
 +                    } catch (final Exception e) {
 +                        final 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
 +            final 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 (final 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 {
 +                                _vmMgr.advanceStop(vm.getUuid(), false);
 +                            } catch (final Exception e) {
 +                                final 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(final 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);
 +
 +        boolean vms_migrating = false;
 +        final List<VMInstanceVO> vms = _haMgr.findTakenMigrationWork();
 +        for (final VMInstanceVO vm : vms) {
 +            if (vm.getHostId() != null && vm.getHostId() == hostId) {
 +                s_logger.warn("Unable to cancel migration because the vm is 
being migrated: " + vm + ", hostId = " + hostId);
 +                vms_migrating = true;
 +            }
 +        }
 +
 +        try {
 +            resourceStateTransitTo(host, 
ResourceState.Event.AdminCancelMaintenance, _nodeId);
 +            _agentMgr.pullAgentOutMaintenance(hostId);
++            retryHostMaintenance.remove(hostId);
 +
 +            // for kvm, need to log into kvm host, restart cloudstack-agent
 +            if ((host.getHypervisorType() == HypervisorType.KVM && 
!vms_migrating) || host.getHypervisorType() == HypervisorType.LXC) {
 +
 +                final boolean sshToAgent = 
Boolean.parseBoolean(_configDao.getValue(Config.KvmSshToAgentEnabled.key()));
 +                if (!sshToAgent) {
 +                    s_logger.info("Configuration tells us not to SSH into 
Agents. Please restart the Agent (" + hostId + ")  manually");
 +                    return true;
 +                }
 +
 +                _hostDao.loadDetails(host);
 +                final String password = host.getDetail("password");
 +                final String username = host.getDetail("username");
 +                if (password == null || username == null) {
 +                    s_logger.debug("Can't find password/username");
 +                    return false;
 +                }
 +                final 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.SSHCmdResult result = 
SSHCmdHelper.sshExecuteCmdOneShot(connection, "service cloudstack-agent 
restart");
 +                    s_logger.debug("cloudstack-agent restart result: " + 
result.toString());
 +                } catch (final SshException e) {
 +                    return false;
 +                }
 +            }
 +
 +            return true;
 +        } catch (final NoTransitionException e) {
 +            s_logger.debug("Cannot transmit host " + host.getId() + "to 
Enabled state", e);
 +            return false;
 +        }
 +    }
 +
 +    private boolean cancelMaintenance(final long hostId) {
 +        try {
 +            final Boolean result = propagateResourceEvent(hostId, 
ResourceState.Event.AdminCancelMaintenance);
 +
 +            if (result != null) {
 +                return result;
 +            }
 +        } catch (final AgentUnavailableException e) {
 +            return false;
 +        }
 +
 +        return doCancelMaintenance(hostId);
 +    }
 +
 +    @Override
 +    public boolean executeUserRequest(final long hostId, final 
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) {
 +            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(final long hostId) {
 +        final 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 || 
host.getHypervisorType() == HypervisorType.LXC) {
 +            _agentMgr.easySend(hostId, new MaintainCommand());
 +        }
 +
 +        _agentMgr.disconnectWithoutInvestigation(hostId, 
Event.ShutdownRequested);
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean umanageHost(final long hostId) {
 +        try {
 +            final Boolean result = propagateResourceEvent(hostId, 
ResourceState.Event.Unmanaged);
 +
 +            if (result != null) {
 +                return result;
 +            }
 +        } catch (final AgentUnavailableException e) {
 +            return false;
 +        }
 +
 +        return doUmanageHost(hostId);
 +    }
 +
 +    private boolean doUpdateHostPassword(final long hostId) {
 +        if (!_agentMgr.isAgentAttached(hostId)) {
 +            return false;
 +        }
 +
 +        DetailVO nv = _hostDetailsDao.findDetail(hostId, 
ApiConstants.USERNAME);
 +        final String username = nv.getValue();
 +        nv = _hostDetailsDao.findDetail(hostId, ApiConstants.PASSWORD);
 +        final String password = nv.getValue();
 +
 +
 +        final HostVO host = _hostDao.findById(hostId);
 +        final String hostIpAddress = host.getPrivateIpAddress();
 +
 +        final UpdateHostPasswordCommand cmd = new 
UpdateHostPasswordCommand(username, password, hostIpAddress);
 +        final Answer answer = _agentMgr.easySend(hostId, cmd);
 +
 +        s_logger.info("Result returned from update host password ==> " + 
answer.getDetails());
 +        return answer.getResult();
 +    }
 +
 +    @Override
 +    public boolean updateClusterPassword(final UpdateHostPasswordCmd command) 
{
 +        final boolean shouldUpdateHostPasswd = 
command.getUpdatePasswdOnHost();
 +        // get agents for the cluster
 +        final List<HostVO> hosts = 
listAllHostsInCluster(command.getClusterId());
 +        for (final HostVO host : hosts) {
 +            try {
 +                final Boolean result = propagateResourceEvent(host.getId(), 
ResourceState.Event.UpdatePassword);
 +                if (result != null) {
 +                    return result;
 +                }
 +            } catch (final AgentUnavailableException e) {
 +                s_logger.error("Agent is not availbale!", e);
 +            }
 +
 +            if (shouldUpdateHostPasswd) {
 +                final boolean isUpdated = doUpdateHostPassword(host.getId());
 +                if (!isUpdated) {
 +                    throw new CloudRuntimeException("CloudStack failed to 
update the password of the Host with UUID / ID ==> " + host.getUuid() + " / " + 
host.getId() + ". Please make sure you are still able to connect to your 
hosts.");
 +                }
 +            }
 +        }
 +
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean updateHostPassword(final UpdateHostPasswordCmd command) {
 +        // update agent attache password
 +        try {
 +            final Boolean result = 
propagateResourceEvent(command.getHostId(), ResourceState.Event.UpdatePassword);
 +            if (result != null) {
 +                return result;
 +            }
 +        } catch (final AgentUnavailableException e) {
 +            s_logger.error("Agent is not availbale!", e);
 +        }
 +
 +        final boolean shouldUpdateHostPasswd = 
command.getUpdatePasswdOnHost();
 +        // If shouldUpdateHostPasswd has been set to false, the method 
doUpdateHostPassword() won't be called.
 +        return shouldUpdateHostPasswd && 
doUpdateHostPassword(command.getHostId());
 +    }
 +
 +    public String getPeerName(final long agentHostId) {
 +
 +        final HostVO host = _hostDao.findById(agentHostId);
 +        if (host != null && host.getManagementServerId() != null) {
 +            if 
(_clusterMgr.getSelfPeerName().equals(Long.toString(host.getManagementServerId())))
 {
 +                return null;
 +            }
 +
 +            return Long.toString(host.getManagementServerId());
 +        }
 +        return null;
 +    }
 +
 +    public Boolean propagateResourceEvent(final long agentId, final 
ResourceState.Event event) throws AgentUnavailableException {
 +        final String msPeer = getPeerName(agentId);
 +        if (msPeer == null) {
 +            return null;
 +        }
 +
 +            s_logger.debug("Propagating resource request event:" + 
event.toString() + " to agent:" + agentId);
 +        final Command[] cmds = new Command[1];
 +        cmds[0] = new PropagateResourceEventCommand(agentId, event);
 +
 +        final String AnsStr = _clusterMgr.execute(msPeer, agentId, 
_gson.toJson(cmds), true);
 +        if (AnsStr == null) {
 +            throw new AgentUnavailableException(agentId);
 +        }
 +
 +        final Answer[] answers = _gson.fromJson(AnsStr, Answer[].class);
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Result for agent change is " + 
answers[0].getResult());
 +        }
 +
 +        return answers[0].getResult();
 +    }
 +
 +    @Override
 +    public boolean maintenanceFailed(final long hostId) {
 +        final 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 (final 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 */
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getResource(), Op.NNULL);
 +        sc.and(sc.entity().getResourceState(), Op.NIN, 
ResourceState.Disabled);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllUpAndEnabledHosts(final Type type, final Long 
clusterId, final Long podId, final long dcId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        if (type != null) {
 +            sc.and(sc.entity().getType(), Op.EQ, type);
 +        }
 +        if (clusterId != null) {
 +            sc.and(sc.entity().getClusterId(), Op.EQ, clusterId);
 +        }
 +        if (podId != null) {
 +            sc.and(sc.entity().getPodId(), Op.EQ, podId);
 +        }
 +        sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        sc.and(sc.entity().getStatus(), Op.EQ, Status.Up);
 +        sc.and(sc.entity().getResourceState(), Op.EQ, ResourceState.Enabled);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllHosts(final Type type, final Long clusterId, 
final Long podId, final long dcId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        if (type != null) {
 +            sc.and(sc.entity().getType(), Op.EQ, type);
 +        }
 +        if (clusterId != null) {
 +            sc.and(sc.entity().getClusterId(), Op.EQ, clusterId);
 +        }
 +        if (podId != null) {
 +            sc.and(sc.entity().getPodId(), Op.EQ, podId);
 +        }
 +        sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllUpHosts(Type type, Long clusterId, Long podId, 
long dcId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        if (type != null) {
 +            sc.and(sc.entity().getType(), Op.EQ, type);
 +        }
 +        if (clusterId != null) {
 +            sc.and(sc.entity().getClusterId(), Op.EQ, clusterId);
 +        }
 +        if (podId != null) {
 +            sc.and(sc.entity().getPodId(), Op.EQ, podId);
 +        }
 +        sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        sc.and(sc.entity().getStatus(), Op.EQ, Status.Up);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllUpAndEnabledNonHAHosts(final Type type, final 
Long clusterId, final Long podId, final long dcId) {
 +        final String haTag = _haMgr.getHaTag();
 +        return _hostDao.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, 
dcId, haTag);
 +    }
 +
 +    @Override
 +    public List<HostVO> findHostByGuid(final long dcId, final String guid) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        sc.and(sc.entity().getGuid(), Op.EQ, guid);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllHostsInCluster(final long clusterId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getClusterId(), Op.EQ, clusterId);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listHostsInClusterByStatus(final long clusterId, 
final Status status) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getClusterId(), Op.EQ, clusterId);
 +        sc.and(sc.entity().getStatus(), Op.EQ, status);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllUpAndEnabledHostsInOneZoneByType(final Type 
type, final long dcId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getType(), Op.EQ, type);
 +        sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        sc.and(sc.entity().getStatus(), Op.EQ, Status.Up);
 +        sc.and(sc.entity().getResourceState(), Op.EQ, ResourceState.Enabled);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllNotInMaintenanceHostsInOneZone(final Type 
type, final Long dcId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        if (dcId != null) {
 +            sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        }
 +        sc.and(sc.entity().getType(), Op.EQ, type);
 +        sc.and(sc.entity().getResourceState(), Op.NIN, 
ResourceState.Maintenance, ResourceState.ErrorInMaintenance, 
ResourceState.PrepareForMaintenance,
 +                ResourceState.Error);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllHostsInOneZoneByType(final Type type, final 
long dcId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getType(), Op.EQ, type);
 +        sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllHostsInAllZonesByType(final Type type) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getType(), Op.EQ, type);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HypervisorType> listAvailHypervisorInZone(final Long hostId, 
final Long zoneId) {
 +        final SearchCriteria<String> sc = _hypervisorsInDC.create();
 +        if (zoneId != null) {
 +            sc.setParameters("dataCenter", zoneId);
 +        }
 +        if (hostId != null) {
 +            // exclude the given host, since we want to check what hypervisor 
is already handled
 +            // in adding this new host
 +            sc.setParameters("id", hostId);
 +        }
 +        sc.setParameters("type", Host.Type.Routing);
 +
 +        // The search is not able to return list of enums, so getting
 +        // list of hypervisors as strings and then converting them to enum
 +        final List<String> hvs = _hostDao.customSearch(sc, null);
 +        final List<HypervisorType> hypervisors = new 
ArrayList<HypervisorType>();
 +        for (final String hv : hvs) {
 +            hypervisors.add(HypervisorType.getType(hv));
 +        }
 +        return hypervisors;
 +    }
 +
 +    @Override
 +    public HostVO findHostByGuid(final String guid) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getGuid(), Op.EQ, guid);
 +        return sc.find();
 +    }
 +
 +    @Override
 +    public HostVO findHostByName(final String name) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getName(), Op.EQ, name);
 +        return sc.find();
 +    }
 +
 +    @Override
 +    public HostStats getHostStatistics(final long hostId) {
 +        final 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()) {
 +            final 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(final long hostId) {
 +        final HostVO host = _hostDao.findById(hostId);
 +        if (host == null) {
 +            return null;
 +        } else {
 +            _hostDao.loadDetails(host);
 +            final DetailVO detail = _hostDetailsDao.findDetail(hostId, 
"guest.os.category.id");
 +            if (detail == null) {
 +                return null;
 +            } else {
 +                return Long.parseLong(detail.getValue());
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public String getHostTags(final long hostId) {
 +        final List<String> hostTags = _hostTagsDao.gethostTags(hostId);
 +        if (hostTags == null) {
 +            return null;
 +        } else {
 +            return StringUtils.listToCsvTags(hostTags);
 +        }
 +    }
 +
 +    @Override
 +    public List<PodCluster> listByDataCenter(final long dcId) {
 +        final List<HostPodVO> pods = _podDao.listByDataCenterId(dcId);
 +        final ArrayList<PodCluster> pcs = new ArrayList<PodCluster>();
 +        for (final HostPodVO pod : pods) {
 +            final List<ClusterVO> clusters = 
_clusterDao.listByPodId(pod.getId());
 +            if (clusters.size() == 0) {
 +                pcs.add(new PodCluster(pod, null));
 +            } else {
 +                for (final ClusterVO cluster : clusters) {
 +                    pcs.add(new PodCluster(pod, cluster));
 +                }
 +            }
 +        }
 +        return pcs;
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllUpAndEnabledHostsInOneZoneByHypervisor(final 
HypervisorType type, final long dcId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getHypervisorType(), Op.EQ, type);
 +        sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        sc.and(sc.entity().getStatus(), Op.EQ, Status.Up);
 +        sc.and(sc.entity().getResourceState(), Op.EQ, ResourceState.Enabled);
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public List<HostVO> listAllUpAndEnabledHostsInOneZone(final long dcId) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +
 +        sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId);
 +        sc.and(sc.entity().getStatus(), Op.EQ, Status.Up);
 +        sc.and(sc.entity().getResourceState(), Op.EQ, ResourceState.Enabled);
 +
 +        return sc.list();
 +    }
 +
 +    @Override
 +    public boolean isHostGpuEnabled(final long hostId) {
 +        final SearchCriteria<HostGpuGroupsVO> sc = _gpuAvailability.create();
 +        sc.setParameters("hostId", hostId);
 +        return _hostGpuGroupsDao.customSearch(sc, null).size() > 0 ? true : 
false;
 +    }
 +
 +    @Override
 +    public List<HostGpuGroupsVO> listAvailableGPUDevice(final long hostId, 
final String groupName, final String vgpuType) {
 +        final Filter searchFilter = new Filter(VGPUTypesVO.class, 
"remainingCapacity", false, null, null);
 +        final SearchCriteria<HostGpuGroupsVO> sc = _gpuAvailability.create();
 +        sc.setParameters("hostId", hostId);
 +        sc.setParameters("groupName", groupName);
 +        sc.setJoinParameters("groupId", "vgpuType", vgpuType);
 +        sc.setJoinParameters("groupId", "remainingCapacity", 0);
 +        return _hostGpuGroupsDao.customSearch(sc, searchFilter);
 +    }
 +
 +    @Override
 +    public boolean isGPUDeviceAvailable(final long hostId, final String 
groupName, final String vgpuType) {
 +        if(!listAvailableGPUDevice(hostId, groupName, vgpuType).isEmpty()) {
 +            return true;
 +        } else {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Host ID: "+ hostId +" does not have GPU 
device available");
 +            }
 +            return false;
 +        }
 +    }
 +
 +    @Override
 +    public GPUDeviceTO getGPUDevice(final long hostId, final String 
groupName, final String vgpuType) {
 +        final List<HostGpuGroupsVO> gpuDeviceList = 
listAvailableGPUDevice(hostId, groupName, vgpuType);
 +
 +        if (CollectionUtils.isEmpty(gpuDeviceList)) {
 +            final String errorMsg = "Host " + hostId + " does not have 
required GPU device or out of capacity. GPU group: " + groupName + ", vGPU 
Type: " + vgpuType;
 +            s_logger.error(errorMsg);
 +            throw new CloudRuntimeException(errorMsg);
 +        }
 +
 +        return new GPUDeviceTO(gpuDeviceList.get(0).getGroupName(), vgpuType, 
null);
 +    }
 +
 +    @Override
 +    public void updateGPUDetails(final long hostId, final HashMap<String, 
HashMap<String, VgpuTypesInfo>> groupDetails) {
 +        // Update GPU group capacity
 +        final TransactionLegacy txn = TransactionLegacy.currentTxn();
 +        txn.start();
 +        _hostGpuGroupsDao.persist(hostId, new 
ArrayList<String>(groupDetails.keySet()));
 +        _vgpuTypesDao.persist(hostId, groupDetails);
 +        txn.commit();
 +    }
 +
 +    @Override
 +    public HashMap<String, HashMap<String, VgpuTypesInfo>> 
getGPUStatistics(final HostVO host) {
 +        final Answer answer = _agentMgr.easySend(host.getId(), new 
GetGPUStatsCommand(host.getGuid(), host.getName()));
 +        if (answer != null && answer instanceof UnsupportedAnswer) {
 +            return null;
 +        }
 +        if (answer == null || !answer.getResult()) {
 +            final String msg = "Unable to obtain GPU stats for host " + 
host.getName();
 +            s_logger.warn(msg);
 +            return null;
 +        } else {
 +            // now construct the result object
 +            if (answer instanceof GetGPUStatsAnswer) {
 +                return ((GetGPUStatsAnswer)answer).getGroupDetails();
 +            }
 +        }
 +        return null;
 +    }
 +
 +    @Override
 +    public HostVO findOneRandomRunningHostByHypervisor(HypervisorType type) {
 +        final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
 +        sc.and(sc.entity().getHypervisorType(), Op.EQ, type);
 +        sc.and(sc.entity().getType(),Op.EQ, Type.Routing);
 +        sc.and(sc.entity().getStatus(), Op.EQ, Status.Up);
 +        sc.and(sc.entity().getResourceState(), Op.EQ, ResourceState.Enabled);
 +        sc.and(sc.entity().getRemoved(), Op.NULL);
 +        List<HostVO> hosts = sc.list();
 +        if (CollectionUtils.isEmpty(hosts)) {
 +            return null;
 +        } else {
 +            Collections.shuffle(hosts, new 
Random(System.currentTimeMillis()));
 +            return hosts.get(0);
 +        }
 +    }
 +
 +    @Override
 +    @DB
 +    @ActionEvent(eventType = EventTypes.EVENT_HOST_RESERVATION_RELEASE, 
eventDescription = "releasing host reservation", async = true)
 +    public boolean releaseHostReservation(final Long hostId) {
 +        try {
 +            return Transaction.execute(new TransactionCallback<Boolean>() {
 +                @Override
 +                public Boolean doInTransaction(final TransactionStatus 
status) {
 +                    final PlannerHostReservationVO reservationEntry = 
_plannerHostReserveDao.findByHostId(hostId);
 +                    if (reservationEntry != null) {
 +                        final long id = reservationEntry.getId();
 +                        final PlannerHostReservationVO hostReservation = 
_plannerHostReserveDao.lockRow(id, true);
 +                        if (hostReservation == null) {
 +                            if (s_logger.isDebugEnabled()) {
 +                                s_logger.debug("Host reservation for host: " 
+ hostId + " does not even exist.  Release reservartion call is ignored.");
 +                            }
 +                            return false;
 +                        }
 +                        hostReservation.setResourceUsage(null);
 +                        _plannerHostReserveDao.persist(hostReservation);
 +                        return true;
 +                    }
 +
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug("Host reservation for host: " + hostId 
+ " does not even exist.  Release reservartion call is ignored.");
 +                    }
 +
 +                    return false;
 +                }
 +            });
 +        } catch (final CloudRuntimeException e) {
 +            throw e;
 +        } catch (final Throwable t) {
 +            s_logger.error("Unable to release host reservation for host: " + 
hostId, t);
 +            return false;
 +        }
 +    }
++
++    @Override
++    public String getConfigComponentName() {
++        return ResourceManagerImpl.class.getSimpleName();
++    }
++
++    @Override
++    public ConfigKey<?>[] getConfigKeys() {
++        return new ConfigKey<?>[] {HostMaintenanceRetries};
++    }
 +}

Reply via email to