/* * 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.storage; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; 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.ModifyStoragePoolCommand; import com.cloud.alert.AlertManager; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceManager; import com.cloud.server.ManagementServer; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.StoragePoolWorkDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.user.dao.UserDao; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.SecondaryStorageVmVO; import com.cloud.vm.UserVmVO; 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.ConsoleProxyDao; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @Component public class StoragePoolAutomationImpl implements StoragePoolAutomation { private static final Logger s_logger = Logger.getLogger(StoragePoolAutomationImpl.class); @Inject protected VirtualMachineManager vmMgr; @Inject protected SecondaryStorageVmDao _secStrgDao; @Inject UserVmDao userVmDao; @Inject protected UserDao _userDao; @Inject protected DomainRouterDao _domrDao; @Inject protected StoragePoolHostDao _storagePoolHostDao; @Inject protected AlertManager _alertMgr; @Inject protected ConsoleProxyDao _consoleProxyDao; @Inject protected StoragePoolWorkDao _storagePoolWorkDao; @Inject PrimaryDataStoreDao primaryDataStoreDao; @Inject DataStoreManager dataStoreMgr; @Inject protected ResourceManager _resourceMgr; @Inject AgentManager agentMgr; @Inject VolumeDao volumeDao; @Inject VMInstanceDao vmDao; @Inject ManagementServer server; @Inject DataStoreProviderManager providerMgr; @Override public boolean maintain(DataStore store) { Long userId = CallContext.current().getCallingUserId(); User user = _userDao.findById(userId); Account account = CallContext.current().getCallingAccount(); StoragePoolVO pool = primaryDataStoreDao.findById(store.getId()); try { List<StoragePoolVO> spes = null; // Handling Zone and Cluster wide storage scopes. // if the storage is ZONE wide then we pass podid and cluster id as null as they will be empty for ZWPS if (pool.getScope() == ScopeType.ZONE) { spes = primaryDataStoreDao.listBy(pool.getDataCenterId(), null, null, ScopeType.ZONE); } else { spes = primaryDataStoreDao.listBy(pool.getDataCenterId(), pool.getPodId(), pool.getClusterId(), ScopeType.CLUSTER); } for (StoragePoolVO sp : spes) { if (sp.getStatus() == StoragePoolStatus.PrepareForMaintenance) { throw new CloudRuntimeException("Only one storage pool in a cluster can be in PrepareForMaintenance mode, " + sp.getId() + " is already in PrepareForMaintenance mode "); } } StoragePool storagePool = (StoragePool)store; //Handeling the Zone wide and cluster wide primay storage List<HostVO> hosts = new ArrayList<HostVO>(); // if the storage scope is ZONE wide, then get all the hosts for which hypervisor ZWSP created to send Modifystoragepoolcommand //TODO: if it's zone wide, this code will list a lot of hosts in the zone, which may cause performance/OOM issue. if (pool.getScope().equals(ScopeType.ZONE)) { if (HypervisorType.Any.equals(pool.getHypervisor())) { hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZone(pool.getDataCenterId()); } else { hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(pool.getHypervisor(), pool.getDataCenterId()); } } else { hosts = _resourceMgr.listHostsInClusterByStatus(pool.getClusterId(), Status.Up); } if (hosts == null || hosts.size() == 0) { pool.setStatus(StoragePoolStatus.Maintenance); primaryDataStoreDao.update(pool.getId(), pool); return true; } else { // set the pool state to prepare for maintenance pool.setStatus(StoragePoolStatus.PrepareForMaintenance); primaryDataStoreDao.update(pool.getId(), pool); } // remove heartbeat for (HostVO host : hosts) { ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(false, storagePool); final Answer answer = agentMgr.easySend(host.getId(), cmd); if (answer == null || !answer.getResult()) { if (s_logger.isDebugEnabled()) { s_logger.debug("ModifyStoragePool false failed due to " + ((answer == null) ? "answer null" : answer.getDetails())); } } else { if (s_logger.isDebugEnabled()) { s_logger.debug("ModifyStoragePool false succeeded"); } } } // check to see if other ps exist // if they do, then we can migrate over the system vms to them // if they dont, then just stop all vms on this one List<StoragePoolVO> upPools = primaryDataStoreDao.listByStatusInZone(pool.getDataCenterId(), StoragePoolStatus.Up); boolean restart = true; if (upPools == null || upPools.size() == 0) { restart = false; } // 2. Get a list of all the ROOT volumes within this storage pool List<VolumeVO> allVolumes = volumeDao.findByPoolId(pool.getId()); // 3. Enqueue to the work queue for (VolumeVO volume : allVolumes) { VMInstanceVO vmInstance = vmDao.findById(volume.getInstanceId()); if (vmInstance == null) { continue; } // enqueue sp work if (vmInstance.getState().equals(State.Running) || vmInstance.getState().equals(State.Starting) || vmInstance.getState().equals(State.Stopping)) { try { StoragePoolWorkVO work = new StoragePoolWorkVO(vmInstance.getId(), pool.getId(), false, false, server.getId()); _storagePoolWorkDao.persist(work); } catch (Exception e) { if (s_logger.isDebugEnabled()) { s_logger.debug("Work record already exists, re-using by re-setting values"); } StoragePoolWorkVO work = _storagePoolWorkDao.findByPoolIdAndVmId(pool.getId(), vmInstance.getId()); work.setStartedAfterMaintenance(false); work.setStoppedForMaintenance(false); work.setManagementServerId(server.getId()); _storagePoolWorkDao.update(work.getId(), work); } } } // 4. Process the queue List<StoragePoolWorkVO> pendingWork = _storagePoolWorkDao.listPendingWorkForPrepareForMaintenanceByPoolId(pool.getId()); for (StoragePoolWorkVO work : pendingWork) { // shut down the running vms VMInstanceVO vmInstance = vmDao.findById(work.getVmId()); if (vmInstance == null) { continue; } // if the instance is of type consoleproxy, call the console // proxy if (vmInstance.getType().equals(VirtualMachine.Type.ConsoleProxy)) { // call the consoleproxymanager ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(vmInstance.getId()); vmMgr.advanceStop(consoleProxy.getUuid(), false); // update work status work.setStoppedForMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); if (restart) { vmMgr.advanceStart(consoleProxy.getUuid(), null, null); // update work status work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } } // if the instance is of type uservm, call the user vm manager if (vmInstance.getType() == VirtualMachine.Type.User) { UserVmVO userVm = userVmDao.findById(vmInstance.getId()); vmMgr.advanceStop(userVm.getUuid(), false); // update work status work.setStoppedForMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } // if the instance is of type secondary storage vm, call the // secondary storage vm manager if (vmInstance.getType().equals(VirtualMachine.Type.SecondaryStorageVm)) { SecondaryStorageVmVO secStrgVm = _secStrgDao.findById(vmInstance.getId()); vmMgr.advanceStop(secStrgVm.getUuid(), false); // update work status work.setStoppedForMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); if (restart) { vmMgr.advanceStart(secStrgVm.getUuid(), null, null); // update work status work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } } // if the instance is of type domain router vm, call the network // manager if (vmInstance.getType().equals(VirtualMachine.Type.DomainRouter)) { DomainRouterVO domR = _domrDao.findById(vmInstance.getId()); vmMgr.advanceStop(domR.getUuid(), false); // update work status work.setStoppedForMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); if (restart) { vmMgr.advanceStart(domR.getUuid(), null, null); // update work status work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } } } } catch (Exception e) { s_logger.error("Exception in enabling primary storage maintenance:", e); pool.setStatus(StoragePoolStatus.ErrorInMaintenance); primaryDataStoreDao.update(pool.getId(), pool); throw new CloudRuntimeException(e.getMessage()); } return true; } @Override public boolean cancelMaintain(DataStore store) { // Change the storage state back to up Long userId = CallContext.current().getCallingUserId(); User user = _userDao.findById(userId); Account account = CallContext.current().getCallingAccount(); StoragePoolVO poolVO = primaryDataStoreDao.findById(store.getId()); StoragePool pool = (StoragePool)store; //Handeling the Zone wide and cluster wide primay storage List<HostVO> hosts = new ArrayList<HostVO>(); // if the storage scope is ZONE wide, then get all the hosts for which hypervisor ZWSP created to send Modifystoragepoolcommand if (poolVO.getScope().equals(ScopeType.ZONE)) { if (HypervisorType.Any.equals(pool.getHypervisor())) { hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZone(pool.getDataCenterId()); } else { hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(poolVO.getHypervisor(), pool.getDataCenterId()); } } else { hosts = _resourceMgr.listHostsInClusterByStatus(pool.getClusterId(), Status.Up); } if (hosts == null || hosts.size() == 0) { return true; } // add heartbeat for (HostVO host : hosts) { ModifyStoragePoolCommand msPoolCmd = new ModifyStoragePoolCommand(true, pool); final Answer answer = agentMgr.easySend(host.getId(), msPoolCmd); if (answer == null || !answer.getResult()) { if (s_logger.isDebugEnabled()) { s_logger.debug("ModifyStoragePool add failed due to " + ((answer == null) ? "answer null" : answer.getDetails())); } } else { if (s_logger.isDebugEnabled()) { s_logger.debug("ModifyStoragePool add secceeded"); } } } // 2. Get a list of pending work for this queue List<StoragePoolWorkVO> pendingWork = _storagePoolWorkDao.listPendingWorkForCancelMaintenanceByPoolId(poolVO.getId()); // 3. work through the queue for (StoragePoolWorkVO work : pendingWork) { try { VMInstanceVO vmInstance = vmDao.findById(work.getVmId()); if (vmInstance == null) { continue; } // if the instance is of type consoleproxy, call the console // proxy if (vmInstance.getType().equals(VirtualMachine.Type.ConsoleProxy)) { ConsoleProxyVO consoleProxy = _consoleProxyDao .findById(vmInstance.getId()); vmMgr.advanceStart(consoleProxy.getUuid(), null, null); // update work queue work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } // if the instance is of type ssvm, call the ssvm manager if (vmInstance.getType().equals( VirtualMachine.Type.SecondaryStorageVm)) { SecondaryStorageVmVO ssVm = _secStrgDao.findById(vmInstance .getId()); vmMgr.advanceStart(ssVm.getUuid(), null, null); // update work queue work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } // if the instance is of type domain router vm, call the network // manager if (vmInstance.getType().equals(VirtualMachine.Type.DomainRouter)) { DomainRouterVO domR = _domrDao.findById(vmInstance.getId()); vmMgr.advanceStart(domR.getUuid(), null, null); // update work queue work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } // if the instance is of type user vm, call the user vm manager if (vmInstance.getType().equals(VirtualMachine.Type.User)) { // check if the vm has a root volume. If not, remove the item from the queue, the vm should be // started only when it has at least one root volume attached to it // don't allow to start vm that doesn't have a root volume if (volumeDao.findByInstanceAndType(vmInstance.getId(), Volume.Type.ROOT).isEmpty()) { _storagePoolWorkDao.remove(work.getId()); } else { UserVmVO userVm = userVmDao.findById(vmInstance.getId()); vmMgr.advanceStart(userVm.getUuid(), null, null); work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } } } catch (Exception e) { s_logger.debug("Failed start vm", e); throw new CloudRuntimeException(e.toString()); } } return false; } }