CLOUDSTACK-8601. VMFS storage added as local storage can be re-added as shared storage.

Fail addition of a VMFS shared storage pool in case it has already been added as local storage in CS.
This commit is contained in:
Likitha Shetty 2015-04-17 18:47:23 +05:30
parent 02c3d14f6e
commit 13a98dd196
11 changed files with 87 additions and 4 deletions

View File

@ -0,0 +1,27 @@
// 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.exception;
public class StorageConflictException extends ManagementServerException {
private static final long serialVersionUID = -294905017911859479L;
public StorageConflictException(String message) {
super(message);
}
}

View File

@ -26,6 +26,7 @@ import com.cloud.storage.template.TemplateProp;
public class ModifyStoragePoolAnswer extends Answer {
StoragePoolInfo poolInfo;
Map<String, TemplateProp> templateInfo;
String localDatastoreName = null;
protected ModifyStoragePoolAnswer() {
}
@ -55,4 +56,12 @@ public class ModifyStoragePoolAnswer extends Answer {
this.templateInfo = templateInfo;
}
public String getLocalDatastoreName() {
return localDatastoreName;
}
public void setLocalDatastoreName(String localDatastoreName) {
this.localDatastoreName = localDatastoreName;
}
}

View File

@ -18,8 +18,10 @@
*/
package org.apache.cloudstack.engine.subsystem.api.storage;
import com.cloud.exception.StorageConflictException;
public interface HypervisorHostListener {
boolean hostConnect(long hostId, long poolId);
boolean hostConnect(long hostId, long poolId) throws StorageConflictException;
boolean hostDisconnected(long hostId, long poolId);
}

View File

@ -117,4 +117,6 @@ public interface PrimaryDataStoreDao extends GenericDao<StoragePoolVO, Long> {
List<StoragePoolVO> findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType);
List<StoragePoolVO> findLocalStoragePoolsByHostAndTags(long hostId, String[] tags);
List<StoragePoolVO> listLocalStoragePoolByPath(long datacenterId, String path);
}

View File

@ -53,6 +53,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
protected final SearchBuilder<StoragePoolVO> DcPodSearch;
protected final SearchBuilder<StoragePoolVO> DcPodAnyClusterSearch;
protected final SearchBuilder<StoragePoolVO> DeleteLvmSearch;
protected final SearchBuilder<StoragePoolVO> DcLocalStorageSearch;
protected final GenericSearchBuilder<StoragePoolVO, Long> StatusCountSearch;
@Inject
@ -115,6 +116,11 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
StatusCountSearch.select(null, Func.COUNT, null);
StatusCountSearch.done();
DcLocalStorageSearch = createSearchBuilder();
DcLocalStorageSearch.and("datacenterId", DcLocalStorageSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
DcLocalStorageSearch.and("path", DcLocalStorageSearch.entity().getPath(), SearchCriteria.Op.EQ);
DcLocalStorageSearch.and("scope", DcLocalStorageSearch.entity().getScope(), SearchCriteria.Op.EQ);
DcLocalStorageSearch.done();
}
@Override
@ -195,6 +201,16 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
return findOneBy(sc);
}
@Override
public List<StoragePoolVO> listLocalStoragePoolByPath(long datacenterId, String path) {
SearchCriteria<StoragePoolVO> sc = DcLocalStorageSearch.create();
sc.setParameters("path", path);
sc.setParameters("datacenterId", datacenterId);
sc.setParameters("scope", ScopeType.HOST);
return listBy(sc);
}
@Override
public List<StoragePoolVO> listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope) {
if (clusterId != null) {

View File

@ -18,6 +18,8 @@
*/
package org.apache.cloudstack.storage.datastore.provider;
import java.util.List;
import javax.inject.Inject;
import org.apache.log4j.Logger;
@ -32,6 +34,7 @@ import com.cloud.agent.api.Answer;
import com.cloud.agent.api.ModifyStoragePoolAnswer;
import com.cloud.agent.api.ModifyStoragePoolCommand;
import com.cloud.alert.AlertManager;
import com.cloud.exception.StorageConflictException;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
@ -52,7 +55,7 @@ public class DefaultHostListener implements HypervisorHostListener {
PrimaryDataStoreDao primaryStoreDao;
@Override
public boolean hostConnect(long hostId, long poolId) {
public boolean hostConnect(long hostId, long poolId) throws StorageConflictException {
StoragePool pool = (StoragePool)this.dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary);
ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool);
final Answer answer = agentMgr.easySend(hostId, cmd);
@ -71,6 +74,17 @@ public class DefaultHostListener implements HypervisorHostListener {
assert (answer instanceof ModifyStoragePoolAnswer) : "Well, now why won't you actually return the ModifyStoragePoolAnswer when it's ModifyStoragePoolCommand? Pool=" +
pool.getId() + "Host=" + hostId;
ModifyStoragePoolAnswer mspAnswer = (ModifyStoragePoolAnswer)answer;
if (mspAnswer.getLocalDatastoreName() != null && pool.isShared()) {
String datastoreName = mspAnswer.getLocalDatastoreName();
List<StoragePoolVO> localStoragePools = this.primaryStoreDao.listLocalStoragePoolByPath(pool.getDataCenterId(), datastoreName);
for (StoragePoolVO localStoragePool : localStoragePools) {
if (datastoreName.equals(localStoragePool.getPath())) {
s_logger.warn("Storage pool: " + pool.getId() + " has already been added as local storage: " + localStoragePool.getName());
throw new StorageConflictException("Cannot add shared storage pool: " + pool.getId() + " because it has already been added as local storage:"
+ localStoragePool.getName());
}
}
}
StoragePoolHostVO poolHost = storagePoolHostDao.findByPoolHost(pool.getId(), hostId);
if (poolHost == null) {

View File

@ -3413,6 +3413,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
long available = summary.getFreeSpace();
Map<String, TemplateProp> tInfo = new HashMap<String, TemplateProp>();
ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(cmd, capacity, available, tInfo);
if (cmd.getAdd() && pool.getType() == StoragePoolType.VMFS) {
answer.setLocalDatastoreName(morDatastore.getValue());
}
return answer;
} catch (Throwable e) {
if (e instanceof RemoteException) {

View File

@ -49,6 +49,7 @@ import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.StoragePoolInfo;
import com.cloud.alert.AlertManager;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.StorageConflictException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
@ -410,6 +411,9 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore
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) {
s_logger.warn("Unable to establish a connection between " + h + " and " + primarystore, e);
}
@ -434,6 +438,9 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore
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) {
s_logger.warn("Unable to establish a connection between " + host + " and " + dataStore, e);
}

View File

@ -29,6 +29,7 @@ import com.cloud.agent.api.StoragePoolInfo;
import com.cloud.agent.manager.Commands;
import com.cloud.capacity.CapacityVO;
import com.cloud.exception.ConnectionException;
import com.cloud.exception.StorageConflictException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.host.Host;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
@ -99,7 +100,7 @@ public interface StorageManager extends StorageService {
boolean registerHostListener(String providerUuid, HypervisorHostListener listener);
void connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException;
void connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException;
void createCapacityEntry(long poolId);

View File

@ -129,6 +129,7 @@ import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceInUseException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.StorageConflictException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
@ -943,7 +944,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
}
@Override
public void connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException {
public void connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException {
StoragePool pool = (StoragePool)_dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary);
assert (pool.isShared()) : "Now, did you actually read the name of this method?";
s_logger.debug("Adding pool " + pool.getName() + " to host " + hostId);

View File

@ -68,6 +68,7 @@ public class CSExceptionErrorCode {
ExceptionErrorCodeMap.put("com.cloud.exception.VirtualMachineMigrationException", 4395);
ExceptionErrorCodeMap.put("com.cloud.async.AsyncCommandQueued", 4540);
ExceptionErrorCodeMap.put("com.cloud.exception.RequestLimitException", 4545);
ExceptionErrorCodeMap.put("com.cloud.exception.StorageConflictException", 4550);
// Have a special error code for ServerApiException when it is
// thrown in a standalone manner when failing to detect any of the above