blob: bbc61e765224b4b7cfb0074a0e45796f9cae3224 [file] [log] [blame]
/*
* 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 org.apache.cloudstack.storage.datastore.lifecycle;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.StoragePoolInfo;
import com.cloud.agent.api.ValidateVcenterDetailsCommand;
import com.cloud.alert.AlertManager;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.StorageConflictException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.resource.ResourceManager;
import com.cloud.server.ManagementServer;
import com.cloud.storage.OCFS2Manager;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolAutomation;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.StoragePoolWorkDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
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;
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
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.HostScope;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStoreLifeCycle {
protected Logger logger = LogManager.getLogger(getClass());
@Inject
protected ResourceManager _resourceMgr;
@Inject
PrimaryDataStoreDao primaryDataStoreDao;
@Inject
protected OCFS2Manager _ocfs2Mgr;
@Inject
DataStoreManager dataStoreMgr;
@Inject
AgentManager agentMgr;
@Inject
StorageManager storageMgr;
@Inject
VolumeDao volumeDao;
@Inject
VMInstanceDao vmDao;
@Inject
ManagementServer server;
@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
PrimaryDataStoreHelper dataStoreHelper;
@Inject
StoragePoolAutomation storagePoolAutmation;
@Inject
protected HostDao _hostDao;
@SuppressWarnings("unchecked")
@Override
public DataStore initialize(Map<String, Object> dsInfos) {
Long clusterId = (Long)dsInfos.get("clusterId");
Long podId = (Long)dsInfos.get("podId");
Long zoneId = (Long)dsInfos.get("zoneId");
String url = (String)dsInfos.get("url");
String providerName = (String)dsInfos.get("providerName");
HypervisorType hypervisorType = (HypervisorType)dsInfos.get("hypervisorType");
if (clusterId != null && podId == null) {
throw new InvalidParameterValueException("Cluster id requires pod id");
}
PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
String tags = (String)dsInfos.get("tags");
Map<String, String> details = (Map<String, String>)dsInfos.get("details");
parameters.setTags(tags);
parameters.setIsTagARule((Boolean)dsInfos.get("isTagARule"));
parameters.setDetails(details);
String scheme = dsInfos.get("scheme").toString();
String storageHost = dsInfos.get("host").toString();
String hostPath = dsInfos.get("hostPath").toString();
String uri = String.format("%s://%s%s", scheme, storageHost, hostPath);
Object localStorage = dsInfos.get("localStorage");
if (localStorage != null) {
hostPath = hostPath.contains("//") ? hostPath.replaceFirst("/", "") : hostPath;
hostPath = hostPath.replace("+", " ");
}
String userInfo = dsInfos.get("userInfo") != null ? dsInfos.get("userInfo").toString() : null;
int port = dsInfos.get("port") != null ? Integer.parseInt(dsInfos.get("port").toString()) : -1;
if (logger.isDebugEnabled()) {
logger.debug("createPool Params @ scheme - " + scheme + " storageHost - " + storageHost + " hostPath - " + hostPath + " port - " + port);
}
if (scheme.equalsIgnoreCase("nfs")) {
if (port == -1) {
port = 2049;
}
parameters.setType(StoragePoolType.NetworkFilesystem);
parameters.setHost(storageHost);
parameters.setPort(port);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("cifs")) {
if (port == -1) {
port = 445;
}
parameters.setType(StoragePoolType.SMB);
parameters.setHost(storageHost);
parameters.setPort(port);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("file")) {
if (port == -1) {
port = 0;
}
parameters.setType(StoragePoolType.Filesystem);
parameters.setHost("localhost");
parameters.setPort(0);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("sharedMountPoint")) {
parameters.setType(StoragePoolType.SharedMountPoint);
parameters.setHost(storageHost);
parameters.setPort(0);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("clvm")) {
parameters.setType(StoragePoolType.CLVM);
parameters.setHost(storageHost);
parameters.setPort(0);
parameters.setPath(hostPath.replaceFirst("/", ""));
} else if (scheme.equalsIgnoreCase("rbd")) {
if (port == -1) {
port = 0;
}
parameters.setType(StoragePoolType.RBD);
parameters.setHost(storageHost);
parameters.setPort(port);
parameters.setPath(hostPath.replaceFirst("/", ""));
parameters.setUserInfo(userInfo);
} else if (scheme.equalsIgnoreCase("PreSetup")) {
if (HypervisorType.VMware.equals(hypervisorType)) {
validateVcenterDetails(zoneId, podId, clusterId,storageHost);
}
parameters.setType(StoragePoolType.PreSetup);
parameters.setHost(storageHost);
parameters.setPort(0);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("DatastoreCluster")) {
if (HypervisorType.VMware.equals(hypervisorType)) {
validateVcenterDetails(zoneId, podId, clusterId,storageHost);
}
parameters.setType(StoragePoolType.DatastoreCluster);
parameters.setHost(storageHost);
parameters.setPort(0);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("iscsi")) {
String[] tokens = hostPath.split("/");
int lun = NumbersUtil.parseInt(tokens[tokens.length - 1], -1);
if (port == -1) {
port = 3260;
}
if (lun != -1) {
if (clusterId == null) {
throw new IllegalArgumentException("IscsiLUN need to have clusters specified");
}
parameters.setType(StoragePoolType.IscsiLUN);
parameters.setHost(storageHost);
parameters.setPort(port);
parameters.setPath(hostPath);
} else {
throw new IllegalArgumentException("iSCSI needs to have LUN number");
}
} else if (scheme.equalsIgnoreCase("iso")) {
if (port == -1) {
port = 2049;
}
parameters.setType(StoragePoolType.ISO);
parameters.setHost(storageHost);
parameters.setPort(port);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("vmfs")) {
parameters.setType(StoragePoolType.VMFS);
parameters.setHost("VMFS datastore: " + hostPath);
parameters.setPort(0);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("ocfs2")) {
port = 7777;
parameters.setType(StoragePoolType.OCFS2);
parameters.setHost("clustered");
parameters.setPort(port);
parameters.setPath(hostPath);
} else if (scheme.equalsIgnoreCase("gluster")) {
if (port == -1) {
port = 24007;
}
parameters.setType(StoragePoolType.Gluster);
parameters.setHost(storageHost);
parameters.setPort(port);
parameters.setPath(hostPath);
} else {
StoragePoolType type = StoragePoolType.valueOf(scheme);
if (type != null) {
parameters.setType(type);
parameters.setHost(storageHost);
parameters.setPort(0);
parameters.setPath(hostPath);
} else {
logger.warn("Unable to figure out the scheme for URI: " + scheme);
throw new IllegalArgumentException("Unable to figure out the scheme for URI: " + scheme);
}
}
if (localStorage == null) {
List<StoragePoolVO> pools = primaryDataStoreDao.listPoolByHostPath(storageHost, hostPath);
if (!pools.isEmpty() && !scheme.equalsIgnoreCase("sharedmountpoint")) {
Long oldPodId = pools.get(0).getPodId();
throw new CloudRuntimeException("Storage pool " + hostPath + " already in use by another pod (id=" + oldPodId + ")");
}
}
Object existingUuid = dsInfos.get("uuid");
String uuid = null;
if (existingUuid != null) {
uuid = (String)existingUuid;
} else if (scheme.equalsIgnoreCase("sharedmountpoint") || scheme.equalsIgnoreCase("clvm")) {
uuid = UUID.randomUUID().toString();
} else if ("PreSetup".equalsIgnoreCase(scheme) && !HypervisorType.VMware.equals(hypervisorType)) {
uuid = hostPath.replace("/", "");
} else {
uuid = UUID.nameUUIDFromBytes((storageHost + hostPath).getBytes()).toString();
}
List<StoragePoolVO> spHandles = primaryDataStoreDao.findIfDuplicatePoolsExistByUUID(uuid);
if ((spHandles != null) && (spHandles.size() > 0)) {
if (logger.isDebugEnabled()) {
logger.debug("Another active pool with the same uuid already exists");
}
throw new CloudRuntimeException("Another active pool with the same uuid already exists");
}
String poolName = (String)dsInfos.get("name");
parameters.setUuid(uuid);
parameters.setZoneId(zoneId);
parameters.setPodId(podId);
parameters.setName(poolName);
parameters.setClusterId(clusterId);
parameters.setProviderName(providerName);
parameters.setHypervisorType(hypervisorType);
return dataStoreHelper.createPrimaryDataStore(parameters);
}
private void validateVcenterDetails(Long zoneId, Long podId, Long clusterId, String storageHost) {
List<HostVO> allHosts =
_resourceMgr.listAllUpHosts(Host.Type.Routing, clusterId, podId, zoneId);
if (allHosts.isEmpty()) {
throw new CloudRuntimeException("No host up to associate a storage pool with in zone: " + zoneId + " pod: " + podId + " cluster: " + clusterId);
}
boolean success = false;
for (HostVO h : allHosts) {
ValidateVcenterDetailsCommand cmd = new ValidateVcenterDetailsCommand(storageHost);
final Answer answer = agentMgr.easySend(h.getId(), cmd);
if (answer != null && answer.getResult()) {
logger.info("Successfully validated vCenter details provided");
return;
} else {
if (answer != null) {
throw new InvalidParameterValueException("Provided vCenter server details does not match with the existing vCenter in zone id: " + zoneId);
} else {
String msg = "Can not validate vCenter through host " + h.getId() + " due to ValidateVcenterDetailsCommand returns null";
logger.warn(msg);
}
}
}
throw new CloudRuntimeException("Could not validate vCenter details through any of the hosts with in zone: " + zoneId + ", pod: " + podId + ", cluster: " + clusterId);
}
protected boolean createStoragePool(long hostId, StoragePool pool) {
logger.debug("creating pool " + pool.getName() + " on host " + hostId);
if (pool.getPoolType() != StoragePoolType.NetworkFilesystem && pool.getPoolType() != StoragePoolType.Filesystem &&
pool.getPoolType() != StoragePoolType.IscsiLUN && pool.getPoolType() != StoragePoolType.Iscsi && pool.getPoolType() != StoragePoolType.VMFS &&
pool.getPoolType() != StoragePoolType.SharedMountPoint && pool.getPoolType() != StoragePoolType.PreSetup && pool.getPoolType() != StoragePoolType.DatastoreCluster && pool.getPoolType() != StoragePoolType.OCFS2 &&
pool.getPoolType() != StoragePoolType.RBD && pool.getPoolType() != StoragePoolType.CLVM && pool.getPoolType() != StoragePoolType.SMB &&
pool.getPoolType() != StoragePoolType.Gluster) {
logger.warn(" Doesn't support storage pool type " + pool.getPoolType());
return false;
}
CreateStoragePoolCommand cmd = new CreateStoragePoolCommand(true, pool);
final Answer answer = agentMgr.easySend(hostId, cmd);
if (answer != null && answer.getResult()) {
storageMgr.updateStorageCapabilities(pool.getId(), false);
return true;
} else {
primaryDataStoreDao.expunge(pool.getId());
String msg = "";
if (answer != null) {
msg = "Can not create storage pool through host " + hostId + " due to " + answer.getDetails();
logger.warn(msg);
} else {
msg = "Can not create storage pool through host " + hostId + " due to CreateStoragePoolCommand returns null";
logger.warn(msg);
}
throw new CloudRuntimeException(msg);
}
}
@Override
public boolean attachCluster(DataStore store, ClusterScope scope) {
PrimaryDataStoreInfo primarystore = (PrimaryDataStoreInfo)store;
// Check if there is host up in this cluster
List<HostVO> allHosts =
_resourceMgr.listAllUpHosts(Host.Type.Routing, primarystore.getClusterId(), primarystore.getPodId(), primarystore.getDataCenterId());
if (allHosts.isEmpty()) {
primaryDataStoreDao.expunge(primarystore.getId());
throw new CloudRuntimeException("No host up to associate a storage pool with in cluster " + primarystore.getClusterId());
}
if (primarystore.getPoolType() == StoragePoolType.OCFS2 && !_ocfs2Mgr.prepareNodes(allHosts, primarystore)) {
logger.warn("Can not create storage pool " + primarystore + " on cluster " + primarystore.getClusterId());
primaryDataStoreDao.expunge(primarystore.getId());
return false;
}
boolean success = false;
for (HostVO h : allHosts) {
success = createStoragePool(h.getId(), primarystore);
if (success) {
break;
}
}
logger.debug("In createPool Adding the pool to each of the hosts");
List<HostVO> poolHosts = new ArrayList<HostVO>();
for (HostVO h : allHosts) {
try {
storageMgr.connectHostToSharedPool(h.getId(), primarystore.getId());
poolHosts.add(h);
} catch (StorageConflictException se) {
primaryDataStoreDao.expunge(primarystore.getId());
throw new CloudRuntimeException("Storage has already been added as local storage");
} catch (Exception e) {
logger.warn("Unable to establish a connection between " + h + " and " + primarystore, e);
}
}
if (poolHosts.isEmpty()) {
logger.warn("No host can access storage pool " + primarystore + " on cluster " + primarystore.getClusterId());
primaryDataStoreDao.expunge(primarystore.getId());
throw new CloudRuntimeException("Failed to access storage pool");
}
dataStoreHelper.attachCluster(store);
return true;
}
@Override
public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) {
List<HostVO> hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(hypervisorType, scope.getScopeId());
logger.debug("In createPool. Attaching the pool to each of the hosts.");
List<HostVO> poolHosts = new ArrayList<HostVO>();
for (HostVO host : hosts) {
try {
storageMgr.connectHostToSharedPool(host.getId(), dataStore.getId());
poolHosts.add(host);
} catch (StorageConflictException se) {
primaryDataStoreDao.expunge(dataStore.getId());
throw new CloudRuntimeException("Storage has already been added as local storage to host: " + host.getName());
} catch (Exception e) {
logger.warn("Unable to establish a connection between " + host + " and " + dataStore, e);
}
}
if (poolHosts.isEmpty()) {
logger.warn("No host can access storage pool " + dataStore + " in this zone.");
primaryDataStoreDao.expunge(dataStore.getId());
throw new CloudRuntimeException("Failed to create storage pool as it is not accessible to hosts.");
}
dataStoreHelper.attachZone(dataStore, hypervisorType);
return true;
}
@Override
public boolean maintain(DataStore dataStore) {
storagePoolAutmation.maintain(dataStore);
dataStoreHelper.maintain(dataStore);
return true;
}
@Override
public boolean cancelMaintain(DataStore store) {
dataStoreHelper.cancelMaintain(store);
storagePoolAutmation.cancelMaintain(store);
return true;
}
@DB
@Override
public boolean deleteDataStore(DataStore store) {
List<StoragePoolHostVO> hostPoolRecords = _storagePoolHostDao.listByPoolId(store.getId());
StoragePool pool = (StoragePool)store;
boolean deleteFlag = false;
// find the hypervisor where the storage is attached to.
HypervisorType hType = null;
if (hostPoolRecords.size() > 0) {
hType = getHypervisorType(hostPoolRecords.get(0).getHostId());
}
// Remove the SR associated with the Xenserver
for (StoragePoolHostVO host : hostPoolRecords) {
DeleteStoragePoolCommand deleteCmd = new DeleteStoragePoolCommand(pool);
final Answer answer = agentMgr.easySend(host.getHostId(), deleteCmd);
if (answer != null && answer.getResult()) {
deleteFlag = true;
// if host is KVM hypervisor then send deleteStoragepoolcmd to all the kvm hosts.
if (HypervisorType.KVM != hType) {
break;
}
} else {
if (answer != null) {
logger.debug("Failed to delete storage pool: " + answer.getResult());
}
}
}
if (!hostPoolRecords.isEmpty() && !deleteFlag) {
throw new CloudRuntimeException("Failed to delete storage pool on host");
}
return dataStoreHelper.deletePrimaryDataStore(store);
}
private HypervisorType getHypervisorType(long hostId) {
HostVO host = _hostDao.findById(hostId);
if (host != null)
return host.getHypervisorType();
return HypervisorType.None;
}
@Override
public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {
DataStore dataStore = dataStoreHelper.attachHost(store, scope, existingInfo);
if(existingInfo.getCapacityBytes() == 0){
try {
storageMgr.connectHostToSharedPool(scope.getScopeId(), dataStore.getId());
} catch (StorageUnavailableException ex) {
logger.error("Storage unavailable ",ex);
} catch (StorageConflictException ex) {
logger.error("Storage already exists ",ex);
}
}
return true;
}
/* (non-Javadoc)
* @see org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle#migrateToObjectStore(org.apache.cloudstack.engine.subsystem.api.storage.DataStore)
*/
@Override
public boolean migrateToObjectStore(DataStore store) {
return false;
}
@Override
public void updateStoragePool(StoragePool storagePool, Map<String, String> details) {
}
@Override
public void enableStoragePool(DataStore dataStore) {
dataStoreHelper.enable(dataStore);
}
@Override
public void disableStoragePool(DataStore dataStore) {
dataStoreHelper.disable(dataStore);
}
}