mirror of https://github.com/apache/cloudstack.git
1037 lines
32 KiB
Java
1037 lines
32 KiB
Java
// Copyright 2012 Citrix Systems, Inc. Licensed under the
|
|
// Apache License, Version 2.0 (the "License"); you may not use this
|
|
// file except in compliance with the License. Citrix Systems, Inc.
|
|
// reserves all rights not expressly granted by the License.
|
|
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// Automatically generated by addcopyright.py at 04/03/2012
|
|
package com.cloud.netapp;
|
|
|
|
import java.io.IOException;
|
|
import java.net.UnknownHostException;
|
|
import java.rmi.ServerException;
|
|
import java.util.ArrayList;
|
|
import java.util.ConcurrentModificationException;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.StringTokenizer;
|
|
|
|
import javax.ejb.Local;
|
|
import javax.naming.ConfigurationException;
|
|
|
|
import netapp.manage.NaAPIFailedException;
|
|
import netapp.manage.NaAuthenticationException;
|
|
import netapp.manage.NaElement;
|
|
import netapp.manage.NaException;
|
|
import netapp.manage.NaProtocolException;
|
|
import netapp.manage.NaServer;
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
import com.cloud.exception.InvalidParameterValueException;
|
|
import com.cloud.exception.ResourceAllocationException;
|
|
import com.cloud.exception.ResourceInUseException;
|
|
import com.cloud.netapp.dao.LunDao;
|
|
import com.cloud.netapp.dao.PoolDao;
|
|
import com.cloud.netapp.dao.VolumeDao;
|
|
import com.cloud.utils.component.Inject;
|
|
import com.cloud.utils.db.DB;
|
|
import com.cloud.utils.db.Transaction;
|
|
import com.cloud.utils.exception.CloudRuntimeException;
|
|
|
|
@Local(value = { NetappManager.class })
|
|
public class NetappManagerImpl implements NetappManager
|
|
{
|
|
public enum Algorithm { roundrobin,leastfull }
|
|
|
|
protected String _name;
|
|
|
|
public static final Logger s_logger = Logger.getLogger(NetappManagerImpl.class.getName());
|
|
@Inject public VolumeDao _volumeDao;
|
|
@Inject public PoolDao _poolDao;
|
|
@Inject public LunDao _lunDao;
|
|
private NetappAllocator _netappAllocator = null;
|
|
|
|
/**
|
|
* Default constructor
|
|
*/
|
|
public NetappManagerImpl(){
|
|
}
|
|
|
|
@Override
|
|
public void createPool(String poolName, String algorithm) throws InvalidParameterValueException
|
|
{
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> createPool ");
|
|
|
|
PoolVO pool = null;
|
|
validAlgorithm(algorithm);
|
|
try {
|
|
pool = new PoolVO(poolName, algorithm);
|
|
_poolDao.persist(pool);
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Response --> createPool:success");
|
|
|
|
} catch (CloudRuntimeException cre){
|
|
pool = _poolDao.findPool(poolName);
|
|
if (pool != null) {
|
|
throw new InvalidParameterValueException("Duplicate Pool Name");
|
|
} else {
|
|
throw cre;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method validates the algorithm used for allocation of the volume
|
|
* @param algorithm -- algorithm type
|
|
* @throws InvalidParameterValueException
|
|
*/
|
|
private void validAlgorithm(String algorithm) throws InvalidParameterValueException {
|
|
//TODO: use enum
|
|
if(!algorithm.equalsIgnoreCase("roundrobin") && !algorithm.equalsIgnoreCase("leastfull")){
|
|
throw new InvalidParameterValueException("Unknown algorithm " + algorithm);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility method to get the netapp server object
|
|
* @param serverIp -- ip address of netapp box
|
|
* @param userName -- username
|
|
* @param password -- password
|
|
* @return
|
|
* @throws UnknownHostException
|
|
*/
|
|
private NaServer getServer(String serverIp, String userName, String password) throws UnknownHostException {
|
|
//Initialize connection to server, and
|
|
//request version 1.3 of the API set
|
|
NaServer s = new NaServer(serverIp, 1, 3);
|
|
s.setStyle(NaServer.STYLE_LOGIN_PASSWORD);
|
|
s.setAdminUser(userName, password);
|
|
|
|
return s;
|
|
}
|
|
|
|
@Override
|
|
public void modifyPool(String poolName, String algorithm) throws InvalidParameterValueException
|
|
{
|
|
validAlgorithm(algorithm);
|
|
PoolVO pool = _poolDao.findPool(poolName);
|
|
|
|
if(pool == null){
|
|
throw new InvalidParameterValueException("Cannot find pool " + poolName);
|
|
}
|
|
|
|
validAlgorithm(algorithm);
|
|
|
|
pool.setAlgorithm(algorithm);
|
|
pool.setName(poolName);
|
|
|
|
_poolDao.update(pool.getId(), pool);
|
|
}
|
|
|
|
@Override
|
|
public void deletePool(String poolName) throws InvalidParameterValueException, ResourceInUseException
|
|
{
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> deletePool ");
|
|
|
|
PoolVO pool = _poolDao.findPool(poolName);
|
|
if(pool == null){
|
|
throw new InvalidParameterValueException("Cannot find pool " + poolName);
|
|
}
|
|
//check if pool is empty
|
|
int volCount = _volumeDao.listVolumes(poolName).size();
|
|
|
|
if(volCount==0){
|
|
_poolDao.remove(pool.getId());
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> deletePool: Success ");
|
|
|
|
} else {
|
|
throw new ResourceInUseException("Cannot delete non-empty pool");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<PoolVO> listPools(){
|
|
return _poolDao.listAll();
|
|
}
|
|
|
|
/**
|
|
* This method destroys the volume on netapp filer
|
|
* @param ipAddress -- ip address of filer
|
|
* @param aggrName -- name of containing aggregate
|
|
* @param volName -- name of volume to destroy
|
|
* @throws ResourceInUseException
|
|
* @throws NaException
|
|
* @throws IOException
|
|
* @throws NaProtocolException
|
|
* @throws NaAPIFailedException
|
|
* @throws NaAuthenticationException
|
|
*/
|
|
@Override
|
|
@DB
|
|
public void destroyVolumeOnFiler(String ipAddress, String aggrName, String volName) throws ServerException, InvalidParameterValueException, ResourceInUseException{
|
|
NaElement xi0;
|
|
NaElement xi1;
|
|
NetappVolumeVO volume = null;
|
|
|
|
volume = _volumeDao.findVolume(ipAddress, aggrName, volName);
|
|
|
|
if(volume==null)
|
|
{
|
|
s_logger.warn("The volume does not exist in our system");
|
|
throw new InvalidParameterValueException("The given tuple:"+ipAddress+","+aggrName+","+volName+" doesn't exist in our system");
|
|
}
|
|
|
|
List<LunVO> lunsOnVol = _lunDao.listLunsByVolId(volume.getId());
|
|
|
|
if(lunsOnVol!=null && lunsOnVol.size()>0)
|
|
{
|
|
s_logger.warn("There are luns on the volume");
|
|
throw new ResourceInUseException("There are luns on the volume");
|
|
}
|
|
|
|
final Transaction txn = Transaction.currentTxn();
|
|
txn.start();
|
|
PoolVO pool = _poolDao.findById(volume.getPoolId());
|
|
if (pool == null) {
|
|
throw new InvalidParameterValueException("Failed to find pool for given volume");
|
|
//FIXME: choose a better exception. this is a db integrity exception
|
|
}
|
|
pool = _poolDao.acquireInLockTable(pool.getId());
|
|
if (pool == null) {
|
|
throw new ConcurrentModificationException("Failed to acquire lock on pool " + volume.getPoolId());
|
|
}
|
|
NaServer s = null;
|
|
try
|
|
{
|
|
s = getServer(volume.getIpAddress(), volume.getUsername(), volume.getPassword());
|
|
//bring the volume down
|
|
xi0 = new NaElement("volume-offline");
|
|
xi0.addNewChild("name",volName);
|
|
s.invokeElem(xi0);
|
|
|
|
//now destroy it
|
|
xi1 = new NaElement("volume-destroy");
|
|
xi1.addNewChild("name",volName);
|
|
s.invokeElem(xi1);
|
|
|
|
//now delete from our records
|
|
_volumeDao.remove(volume.getId());
|
|
txn.commit();
|
|
|
|
} catch (UnknownHostException uhe) {
|
|
s_logger.warn("Unable to delete volume on filer " , uhe);
|
|
throw new ServerException("Unable to delete volume on filer", uhe);
|
|
}catch (NaAPIFailedException naf) {
|
|
s_logger.warn("Unable to delete volume on filer " , naf);
|
|
if( naf.getErrno() == 13040 ){
|
|
s_logger.info("Deleting the volume: " + volName);
|
|
_volumeDao.remove(volume.getId());
|
|
txn.commit();
|
|
}
|
|
|
|
throw new ServerException("Unable to delete volume on filer", naf);
|
|
}
|
|
catch (NaException nae) {
|
|
txn.rollback();
|
|
s_logger.warn("Unable to delete volume on filer " , nae);
|
|
throw new ServerException("Unable to delete volume on filer", nae);
|
|
} catch (IOException ioe) {
|
|
txn.rollback();
|
|
s_logger.warn("Unable to delete volume on filer " , ioe);
|
|
throw new ServerException("Unable to delete volume on filer", ioe);
|
|
}
|
|
finally
|
|
{
|
|
if (pool != null) {
|
|
_poolDao.releaseFromLockTable(pool.getId());
|
|
}
|
|
if (s != null)
|
|
s.close();
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* This method creates a volume on netapp filer
|
|
* @param ipAddress -- ip address of the filer
|
|
* @param aggName -- name of aggregate
|
|
* @param poolName -- name of pool
|
|
* @param volName -- name of volume
|
|
* @param volSize -- size of volume to be created
|
|
* @param snapshotPolicy -- associated snapshot policy for volume
|
|
* @param snapshotReservation -- associated reservation for snapshots
|
|
* @param username -- username
|
|
* @param password -- password
|
|
* @throws UnknownHostException
|
|
* @throws InvalidParameterValueException
|
|
*/
|
|
@Override
|
|
@DB
|
|
public void createVolumeOnFiler(String ipAddress, String aggName, String poolName, String volName, String volSize, String snapshotPolicy, Integer snapshotReservation, String username, String password) throws UnknownHostException, ServerException, InvalidParameterValueException
|
|
{
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> createVolume "+"serverIp:"+ipAddress);
|
|
|
|
boolean snapPolicy = false;
|
|
boolean snapshotRes = false;
|
|
boolean volumeCreated = false;
|
|
|
|
NaServer s = getServer(ipAddress, username, password);
|
|
|
|
NaElement xi = new NaElement("volume-create");
|
|
xi.addNewChild("volume", volName);
|
|
xi.addNewChild("containing-aggr-name",aggName);
|
|
xi.addNewChild("size",volSize);
|
|
|
|
NaElement xi1 = new NaElement("snapshot-set-reserve");
|
|
if(snapshotReservation!=null)
|
|
{
|
|
snapshotRes = true;
|
|
xi1.addNewChild("percentage",snapshotReservation.toString());
|
|
xi1.addNewChild("volume",volName);
|
|
}
|
|
|
|
NaElement xi2 = new NaElement("snapshot-set-schedule");
|
|
|
|
if(snapshotPolicy!=null)
|
|
{
|
|
snapPolicy = true;
|
|
|
|
String weeks = null;
|
|
String days = null;
|
|
String hours = null;
|
|
String whichHours = null;
|
|
String minutes = null;
|
|
String whichMinutes = null;
|
|
|
|
StringTokenizer s1 = new StringTokenizer(snapshotPolicy," ");
|
|
|
|
//count=4: weeks days hours@csi mins@csi
|
|
//count=3: weeks days hours@csi
|
|
//count=2: weeks days
|
|
//count=1: weeks
|
|
|
|
if(s1.hasMoreTokens())
|
|
{
|
|
weeks = s1.nextToken();
|
|
}
|
|
if(weeks!=null && s1.hasMoreTokens())
|
|
{
|
|
days = s1.nextToken();
|
|
}
|
|
if(days!=null && s1.hasMoreTokens())
|
|
{
|
|
String[] hoursArr = s1.nextToken().split("@");
|
|
hours = hoursArr[0];
|
|
whichHours = hoursArr[1];
|
|
}
|
|
if(hours!=null && s1.hasMoreTokens())
|
|
{
|
|
String[] minsArr = s1.nextToken().split("@");
|
|
minutes = minsArr[0];
|
|
whichMinutes = minsArr[1];
|
|
}
|
|
|
|
if(weeks!=null)
|
|
xi2.addNewChild("weeks",weeks);
|
|
if(days!=null)
|
|
xi2.addNewChild("days",days);
|
|
if(hours!=null)
|
|
xi2.addNewChild("hours",hours);
|
|
if(minutes!=null)
|
|
xi2.addNewChild("minutes",minutes);
|
|
xi2.addNewChild("volume",volName);
|
|
|
|
if(whichHours!=null)
|
|
xi2.addNewChild("which-hours",whichHours);
|
|
if(whichMinutes!=null)
|
|
xi2.addNewChild("which-minutes",whichMinutes);
|
|
}
|
|
Long volumeId = null;
|
|
|
|
final Transaction txn = Transaction.currentTxn();
|
|
txn.start();
|
|
NetappVolumeVO volume = null;
|
|
volume = _volumeDao.findVolume(ipAddress, aggName, volName);
|
|
|
|
if(volume != null) {
|
|
throw new InvalidParameterValueException("The volume for the given ipAddress/aggregateName/volumeName tuple already exists");
|
|
}
|
|
PoolVO pool = _poolDao.findPool(poolName);
|
|
if (pool == null) {
|
|
throw new InvalidParameterValueException("Cannot find pool " + poolName);
|
|
}
|
|
pool = _poolDao.acquireInLockTable(pool.getId());
|
|
if (pool == null) {
|
|
s_logger.warn("Failed to acquire lock on pool " + poolName);
|
|
throw new ConcurrentModificationException("Failed to acquire lock on pool " + poolName);
|
|
}
|
|
volume = new NetappVolumeVO(ipAddress, aggName, pool.getId(), volName, volSize, "", 0, username, password,0,pool.getName());
|
|
volume = _volumeDao.persist(volume);
|
|
|
|
volumeId = volume.getId();
|
|
try {
|
|
s.invokeElem(xi);
|
|
volumeCreated = true;
|
|
|
|
if(snapshotRes)
|
|
{
|
|
s.invokeElem(xi1);
|
|
volume.setSnapshotReservation(snapshotReservation);
|
|
_volumeDao.update(volumeId, volume);
|
|
}
|
|
|
|
if(snapPolicy)
|
|
{
|
|
s.invokeElem(xi2);
|
|
volume.setSnapshotPolicy(snapshotPolicy);
|
|
_volumeDao.update(volumeId, volume);
|
|
}
|
|
txn.commit();
|
|
} catch (NaException nae) {
|
|
//zapi call failed, log and throw e
|
|
s_logger.warn("Failed to create volume on the netapp filer:", nae);
|
|
txn.rollback();
|
|
if(volumeCreated) {
|
|
try {
|
|
deleteRogueVolume(volName, s);//deletes created volume on filer
|
|
} catch (NaException e) {
|
|
s_logger.warn("Failed to cleanup created volume whilst rolling back on the netapp filer:", e);
|
|
throw new ServerException("Unable to create volume via cloudtools."+"Failed to cleanup created volume on netapp filer whilst rolling back on the cloud db:", e);
|
|
} catch (IOException e) {
|
|
s_logger.warn("Failed to cleanup created volume whilst rolling back on the netapp filer:", e);
|
|
throw new ServerException("Unable to create volume via cloudtools."+"Failed to cleanup created volume on netapp filer whilst rolling back on the cloud db:", e);
|
|
}
|
|
}
|
|
throw new ServerException("Unable to create volume", nae);
|
|
} catch (IOException ioe) {
|
|
s_logger.warn("Failed to create volume on the netapp filer:", ioe);
|
|
txn.rollback();
|
|
if(volumeCreated) {
|
|
try {
|
|
deleteRogueVolume(volName, s);//deletes created volume on filer
|
|
} catch (NaException e) {
|
|
s_logger.warn("Failed to cleanup created volume whilst rolling back on the netapp filer:", e);
|
|
throw new ServerException("Unable to create volume via cloudtools."+"Failed to cleanup created volume on netapp filer whilst rolling back on the cloud db:", e);
|
|
} catch (IOException e) {
|
|
s_logger.warn("Failed to cleanup created volume whilst rolling back on the netapp filer:", e);
|
|
throw new ServerException("Unable to create volume via cloudtools."+"Failed to cleanup created volume on netapp filer whilst rolling back on the cloud db:", e);
|
|
}
|
|
}
|
|
throw new ServerException("Unable to create volume", ioe);
|
|
}
|
|
finally{
|
|
if (s != null)
|
|
s.close();
|
|
if (pool != null)
|
|
_poolDao.releaseFromLockTable(pool.getId());
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is primarily used to cleanup volume created on the netapp filer, when createVol api command fails at snapshot reservation.
|
|
* We roll back the db record, but the record on the netapp box still exists. We clean up that record using this helper method.
|
|
* @param volName
|
|
* @param s -- server reference
|
|
* @throws NaException
|
|
* @throws IOException
|
|
*/
|
|
private void deleteRogueVolume(String volName, NaServer s) throws NaException, IOException {
|
|
//bring the volume down
|
|
NaElement xi0 = new NaElement("volume-offline");
|
|
xi0.addNewChild("name",volName);
|
|
s.invokeElem(xi0);
|
|
|
|
//now destroy it
|
|
NaElement xi1 = new NaElement("volume-destroy");
|
|
xi1.addNewChild("name",volName);
|
|
s.invokeElem(xi1);
|
|
}
|
|
|
|
/**
|
|
* This method lists all the volumes by pool name
|
|
* @param poolName
|
|
* @return -- volumes in that pool
|
|
*/
|
|
@Override
|
|
public List<NetappVolumeVO> listVolumesOnFiler(String poolName){
|
|
|
|
List<NetappVolumeVO> vols = _volumeDao.listVolumesAscending(poolName);
|
|
|
|
for(NetappVolumeVO vol : vols){
|
|
try {
|
|
String snapScheduleOnFiler = returnSnapshotSchedule(vol);
|
|
vol.setSnapshotPolicy(snapScheduleOnFiler);
|
|
|
|
} catch (ServerException e) {
|
|
s_logger.warn("Error trying to get snapshot schedule for volume"+vol.getVolumeName());
|
|
}
|
|
}
|
|
return vols;
|
|
}
|
|
|
|
|
|
/**
|
|
* Utility method to return snapshot schedule for a volume
|
|
* @param vol -- volume for the snapshot schedule creation
|
|
* @return -- the snapshot schedule
|
|
* @throws ServerException
|
|
*/
|
|
private String returnSnapshotSchedule(NetappVolumeVO vol) throws ServerException{
|
|
|
|
NaElement xi = new NaElement("snapshot-get-schedule");
|
|
xi.addNewChild("volume", vol.getVolumeName());
|
|
NaServer s = null;
|
|
try {
|
|
s = getServer(vol.getIpAddress(), vol.getUsername(), vol.getPassword());
|
|
NaElement xo = s.invokeElem(xi);
|
|
String weeks = xo.getChildContent("weeks");
|
|
String days = xo.getChildContent("days");
|
|
String hours = xo.getChildContent("hours");
|
|
String minutes = xo.getChildContent("minutes");
|
|
String whichHours = xo.getChildContent("which-hours");
|
|
String whichMinutes = xo.getChildContent("which-minutes");
|
|
|
|
StringBuilder sB = new StringBuilder();
|
|
sB.append(weeks).append(" ").append(days).append(" ").append(hours).append("@").append(whichHours).append(" ").append(minutes).append("@").append(whichMinutes);
|
|
return sB.toString();
|
|
} catch (NaException nae) {
|
|
s_logger.warn("Failed to get volume size ", nae);
|
|
throw new ServerException("Failed to get volume size", nae);
|
|
} catch (IOException ioe) {
|
|
s_logger.warn("Failed to get volume size ", ioe);
|
|
throw new ServerException("Failed to get volume size", ioe);
|
|
}
|
|
finally{
|
|
if (s != null)
|
|
s.close();
|
|
}
|
|
}
|
|
/**
|
|
* This method returns the ascending order list of volumes based on their ids
|
|
* @param poolName -- name of pool
|
|
* @return -- ascending ordered list of volumes based on ids
|
|
*/
|
|
@Override
|
|
public List<NetappVolumeVO> listVolumesAscending(String poolName){
|
|
return _volumeDao.listVolumesAscending(poolName);
|
|
}
|
|
|
|
/**
|
|
* This method returns the available size on the volume in terms of bytes
|
|
* @param volName -- name of volume
|
|
* @param userName -- username
|
|
* @param password -- password
|
|
* @param serverIp -- ip address of filer
|
|
* @throws UnknownHostException
|
|
* @return-- available size on the volume in terms of bytes; return -1 if volume is offline
|
|
* @throws ServerException
|
|
*/
|
|
@Override
|
|
public long returnAvailableVolumeSize(String volName, String userName, String password, String serverIp) throws ServerException {
|
|
long availableSize = 0;
|
|
|
|
NaElement xi = new NaElement("volume-list-info");
|
|
xi.addNewChild("volume", volName);
|
|
NaServer s = null;
|
|
String volumeState = null;
|
|
try {
|
|
s = getServer(serverIp, userName, password);
|
|
NaElement xo = s.invokeElem(xi);
|
|
List volList = xo.getChildByName("volumes").getChildren();
|
|
Iterator volIter = volList.iterator();
|
|
while(volIter.hasNext()){
|
|
NaElement volInfo=(NaElement)volIter.next();
|
|
availableSize = volInfo.getChildLongValue("size-available", -1);
|
|
volumeState = volInfo.getChildContent("state");
|
|
}
|
|
|
|
if (volumeState != null) {
|
|
return volumeState.equalsIgnoreCase("online") ? availableSize : -1; //return -1 if volume is offline
|
|
}
|
|
else {
|
|
//catch all
|
|
//volume state unreported
|
|
return -1; // as good as volume offline
|
|
}
|
|
|
|
|
|
} catch (NaException nae) {
|
|
s_logger.warn("Failed to get volume size ", nae);
|
|
throw new ServerException("Failed to get volume size", nae);
|
|
} catch (IOException ioe) {
|
|
s_logger.warn("Failed to get volume size ", ioe);
|
|
throw new ServerException("Failed to get volume size", ioe);
|
|
}
|
|
finally{
|
|
if (s != null)
|
|
s.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method creates a lun on the netapp filer
|
|
* @param poolName -- name of the pool
|
|
* @param lunSize -- size of the lun to be created
|
|
* @return -- lun path
|
|
* @throws IOException
|
|
* @throws ResourceAllocationException
|
|
* @throws NaException
|
|
*/
|
|
@Override
|
|
@DB
|
|
public String[] createLunOnFiler(String poolName, Long lunSize) throws ServerException, InvalidParameterValueException, ResourceAllocationException {
|
|
String[] result = new String[3];
|
|
StringBuilder lunName = new StringBuilder("lun-");
|
|
LunVO lun = null;
|
|
final Transaction txn = Transaction.currentTxn();
|
|
txn.start();
|
|
PoolVO pool = _poolDao.findPool(poolName);
|
|
|
|
if(pool == null){
|
|
throw new InvalidParameterValueException("Cannot find pool " + poolName);
|
|
}
|
|
|
|
if (lunSize <= 0) {
|
|
throw new InvalidParameterValueException("Please specify a valid lun size in Gb");
|
|
}
|
|
|
|
String algorithm = pool.getAlgorithm();
|
|
NetappVolumeVO selectedVol = null;
|
|
|
|
//sanity check
|
|
int numVolsInPool = _volumeDao.listVolumes(poolName).size();
|
|
|
|
if (numVolsInPool == 0)
|
|
{
|
|
throw new InvalidParameterValueException("No volumes exist in the given pool");
|
|
}
|
|
pool = _poolDao.acquireInLockTable(pool.getId());
|
|
if (pool == null) {
|
|
s_logger.warn("Failed to acquire lock on the pool " + poolName);
|
|
return result;
|
|
}
|
|
NaServer s = null;
|
|
|
|
try
|
|
{
|
|
if(algorithm == null || algorithm.equals(Algorithm.roundrobin.toString()))
|
|
{
|
|
selectedVol = _netappAllocator.chooseVolumeFromPool(poolName, lunSize);
|
|
}
|
|
else if(algorithm.equals(Algorithm.leastfull.toString()))
|
|
{
|
|
|
|
selectedVol = _netappAllocator.chooseLeastFullVolumeFromPool(poolName, lunSize);
|
|
}
|
|
|
|
if(selectedVol == null)
|
|
{
|
|
throw new ServerException("Could not find a suitable volume to create lun on");
|
|
}
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> createLun "+"serverIp:"+selectedVol.getIpAddress());
|
|
|
|
StringBuilder exportPath = new StringBuilder("/vol/");
|
|
exportPath.append(selectedVol.getVolumeName());
|
|
exportPath.append("/");
|
|
|
|
lun = new LunVO(exportPath.toString(), selectedVol.getId(), lunSize, "","");
|
|
lun = _lunDao.persist(lun);
|
|
|
|
//Lun id created: 6 digits right justified eg. 000045
|
|
String lunIdStr = lun.getId().toString();
|
|
String zeroStr = "000000";
|
|
int length = lunIdStr.length();
|
|
int offset = 6-length;
|
|
StringBuilder lunIdOnPath = new StringBuilder();
|
|
lunIdOnPath.append(zeroStr.substring(0, offset));
|
|
lunIdOnPath.append(lunIdStr);
|
|
exportPath.append("lun-").append(lunIdOnPath.toString());
|
|
|
|
lunName.append(lunIdOnPath.toString());
|
|
|
|
//update lun name
|
|
lun.setLunName(lunName.toString());
|
|
_lunDao.update(lun.getId(), lun);
|
|
|
|
NaElement xi;
|
|
NaElement xi1;
|
|
|
|
long lSizeBytes = 1L*lunSize*1024*1024*1024; //This prevents integer overflow
|
|
Long lunSizeBytes = new Long(lSizeBytes);
|
|
|
|
s = getServer(selectedVol.getIpAddress(), selectedVol.getUsername(),selectedVol.getPassword());
|
|
|
|
//create lun
|
|
xi = new NaElement("lun-create-by-size");
|
|
xi.addNewChild("ostype","linux");
|
|
xi.addNewChild("path",exportPath.toString());
|
|
xi.addNewChild("size", (lunSizeBytes.toString()));
|
|
|
|
s.invokeElem(xi);
|
|
|
|
try
|
|
{
|
|
//now create an igroup
|
|
xi1 = new NaElement("igroup-create");
|
|
xi1.addNewChild("initiator-group-name", lunName .toString());
|
|
xi1.addNewChild("initiator-group-type", "iscsi");
|
|
xi1.addNewChild("os-type", "linux");
|
|
s.invokeElem(xi1);
|
|
}catch(NaAPIFailedException e)
|
|
{
|
|
if(e.getErrno() == 9004)
|
|
{
|
|
//igroup already exists hence no error
|
|
s_logger.warn("Igroup already exists");
|
|
}
|
|
}
|
|
|
|
//get target iqn
|
|
NaElement xi4 = new NaElement("iscsi-node-get-name");
|
|
NaElement xo = s.invokeElem(xi4);
|
|
String iqn = xo.getChildContent("node-name");
|
|
|
|
lun.setTargetIqn(iqn);
|
|
_lunDao.update(lun.getId(), lun);
|
|
|
|
//create lun mapping
|
|
//now map the lun to the igroup
|
|
NaElement xi3 = new NaElement("lun-map");
|
|
xi3.addNewChild("force", "true");
|
|
xi3.addNewChild("initiator-group", lunName.toString());
|
|
xi3.addNewChild("path", lun.getPath() + lun.getLunName());
|
|
|
|
xi3.addNewChild("lun-id", lunIdStr);
|
|
s.invokeElem(xi3);
|
|
|
|
txn.commit();
|
|
//set the result
|
|
result[0] = lunName.toString();//lunname
|
|
result[1] = iqn;//iqn
|
|
result[2] = selectedVol.getIpAddress();
|
|
|
|
return result;
|
|
|
|
}
|
|
catch (NaAPIFailedException naf)
|
|
{
|
|
if(naf.getErrno() == 9023){ //lun is already mapped to this group
|
|
result[0] = lunName.toString();//lunname;
|
|
result[1] = lun.getTargetIqn();//iqn
|
|
result[2] = selectedVol.getIpAddress();
|
|
return result;
|
|
}
|
|
if(naf.getErrno() == 9024){ //another lun mapped at this group
|
|
result[0] = lunName.toString();//lunname;
|
|
result[1] = lun.getTargetIqn();//iqn
|
|
result[2] = selectedVol.getIpAddress();
|
|
return result;
|
|
}
|
|
}
|
|
catch (NaException nae)
|
|
{
|
|
txn.rollback();
|
|
throw new ServerException("Unable to create LUN", nae);
|
|
}
|
|
catch (IOException ioe)
|
|
{
|
|
txn.rollback();
|
|
throw new ServerException("Unable to create LUN", ioe);
|
|
}
|
|
finally
|
|
{
|
|
if (pool != null) {
|
|
_poolDao.releaseFromLockTable(pool.getId());
|
|
}
|
|
if (s != null) {
|
|
s.close();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method destroys a lun on the netapp filer
|
|
* @param lunName -- name of the lun to be destroyed
|
|
*/
|
|
@Override
|
|
@DB
|
|
public void destroyLunOnFiler(String lunName) throws InvalidParameterValueException, ServerException{
|
|
|
|
final Transaction txn = Transaction.currentTxn();
|
|
txn.start();
|
|
|
|
LunVO lun = _lunDao.findByName(lunName);
|
|
|
|
if(lun == null)
|
|
throw new InvalidParameterValueException("Cannot find lun");
|
|
|
|
NetappVolumeVO vol = _volumeDao.acquireInLockTable(lun.getVolumeId());
|
|
if (vol == null) {
|
|
s_logger.warn("Failed to lock volume id= " + lun.getVolumeId());
|
|
return;
|
|
}
|
|
NaServer s = null;
|
|
try {
|
|
s = getServer(vol.getIpAddress(), vol.getUsername(), vol.getPassword());
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> destroyLun "+":serverIp:"+vol.getIpAddress());
|
|
|
|
try {
|
|
//Unmap lun
|
|
NaElement xi2 = new NaElement("lun-unmap");
|
|
xi2.addNewChild("initiator-group", lunName);
|
|
xi2.addNewChild("path", lun.getPath()+lun.getLunName());
|
|
s.invokeElem(xi2);
|
|
} catch (NaAPIFailedException naf) {
|
|
if(naf.getErrno()==9016)
|
|
s_logger.warn("no map exists excpn 9016 caught in deletelun, continuing with delete");
|
|
}
|
|
|
|
//destroy lun
|
|
NaElement xi = new NaElement("lun-destroy");
|
|
xi.addNewChild("force","true");
|
|
xi.addNewChild("path", lun.getPath()+lun.getLunName());
|
|
s.invokeElem(xi);
|
|
|
|
//destroy igroup
|
|
NaElement xi1 = new NaElement("igroup-destroy");
|
|
//xi1.addNewChild("force","true");
|
|
xi1.addNewChild("initiator-group-name",lunName);
|
|
s.invokeElem(xi1);
|
|
|
|
_lunDao.remove(lun.getId());
|
|
txn.commit();
|
|
} catch(UnknownHostException uhe) {
|
|
txn.rollback();
|
|
s_logger.warn("Failed to delete lun", uhe);
|
|
throw new ServerException("Failed to delete lun", uhe);
|
|
} catch ( IOException ioe) {
|
|
txn.rollback();
|
|
s_logger.warn("Failed to delete lun", ioe);
|
|
throw new ServerException("Failed to delete lun", ioe);
|
|
}catch (NaAPIFailedException naf) {
|
|
if(naf.getErrno() == 9017){//no such group exists excpn
|
|
s_logger.warn("no such group exists excpn 9017 caught in deletelun, continuing with delete");
|
|
_lunDao.remove(lun.getId());
|
|
txn.commit();
|
|
}else if(naf.getErrno() == 9029){//LUN maps for this initiator group exist
|
|
s_logger.warn("LUN maps for this initiator group exist errno 9029 caught in deletelun, continuing with delete");
|
|
_lunDao.remove(lun.getId());
|
|
txn.commit();
|
|
}else{
|
|
txn.rollback();
|
|
s_logger.warn("Failed to delete lun", naf);
|
|
throw new ServerException("Failed to delete lun", naf);
|
|
}
|
|
|
|
}
|
|
catch (NaException nae) {
|
|
txn.rollback();
|
|
s_logger.warn("Failed to delete lun", nae);
|
|
throw new ServerException("Failed to delete lun", nae);
|
|
}
|
|
finally{
|
|
if (vol != null) {
|
|
_volumeDao.releaseFromLockTable(vol.getId());
|
|
}
|
|
if (s != null)
|
|
s.close();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* This method lists the luns on the netapp filer
|
|
* @param volId -- id of the containing volume
|
|
* @return -- list of netapp luns
|
|
* @throws NaException
|
|
* @throws IOException
|
|
*/
|
|
@Override
|
|
public List<LunVO> listLunsOnFiler(String poolName)
|
|
{
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> listLunsOnFiler ");
|
|
|
|
List<LunVO> luns = new ArrayList<LunVO>();
|
|
|
|
List<NetappVolumeVO> vols = _volumeDao.listVolumes(poolName);
|
|
|
|
for(NetappVolumeVO vol : vols)
|
|
{
|
|
luns.addAll(_lunDao.listLunsByVolId(vol.getId()));
|
|
}
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Response --> listLunsOnFiler:success");
|
|
|
|
return luns;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method disassociates a lun from the igroup on the filer
|
|
* @param iGroup -- igroup name
|
|
* @param lunName -- lun name
|
|
*/
|
|
@Override
|
|
public void disassociateLun(String iGroup, String lunName) throws ServerException, InvalidParameterValueException
|
|
{
|
|
NaElement xi;
|
|
LunVO lun = _lunDao.findByName(lunName);
|
|
|
|
if(lun == null)
|
|
throw new InvalidParameterValueException("Cannot find LUN " + lunName);
|
|
|
|
NetappVolumeVO vol = _volumeDao.findById(lun.getVolumeId());
|
|
NaServer s = null;
|
|
try {
|
|
s = getServer(vol.getIpAddress(), vol.getUsername(), vol.getPassword());
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> disassociateLun "+":serverIp:"+vol.getIpAddress());
|
|
|
|
xi = new NaElement("igroup-remove");
|
|
xi.addNewChild("force", "true");
|
|
xi.addNewChild("initiator", iGroup);
|
|
xi.addNewChild("initiator-group-name", lunName);
|
|
s.invokeElem(xi);
|
|
|
|
} catch(UnknownHostException uhe) {
|
|
throw new ServerException("Failed to disassociate lun", uhe);
|
|
} catch ( IOException ioe) {
|
|
throw new ServerException("Failed to disassociate lun", ioe);
|
|
} catch (NaException nae) {
|
|
throw new ServerException("Failed to disassociate lun", nae);
|
|
} finally{
|
|
if (s != null)
|
|
s.close();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* This method associates a lun to a particular igroup
|
|
* @param iqn
|
|
* @param iGroup
|
|
* @param lunName
|
|
*/
|
|
@Override
|
|
public String[] associateLun(String guestIqn, String lunName) throws ServerException, InvalidParameterValueException
|
|
|
|
{
|
|
NaElement xi2;
|
|
|
|
//get lun id from path
|
|
String[] splitLunName = lunName.split("-");
|
|
String[] returnVal = new String[3];
|
|
if(splitLunName.length != 2)
|
|
throw new InvalidParameterValueException("The lun id is malformed");
|
|
|
|
String lunIdStr = splitLunName[1];
|
|
|
|
Long lId = new Long(lunIdStr);
|
|
|
|
LunVO lun = _lunDao.findById(lId);
|
|
|
|
if(lun == null)
|
|
throw new InvalidParameterValueException("Cannot find LUN " + lunName);
|
|
|
|
NetappVolumeVO vol = _volumeDao.findById(lun.getVolumeId());
|
|
|
|
//assert(vol != null);
|
|
|
|
returnVal[0] = lunIdStr;
|
|
returnVal[1] = lun.getTargetIqn();
|
|
returnVal[2] = vol.getIpAddress();
|
|
|
|
NaServer s = null;
|
|
|
|
try
|
|
{
|
|
s = getServer(vol.getIpAddress(), vol.getUsername(), vol.getPassword());
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("Request --> associateLun "+":serverIp:"+vol.getIpAddress());
|
|
|
|
//add iqn to the group
|
|
xi2 = new NaElement("igroup-add");
|
|
xi2.addNewChild("force", "true");
|
|
xi2.addNewChild("initiator", guestIqn);
|
|
xi2.addNewChild("initiator-group-name", lunName);
|
|
s.invokeElem(xi2);
|
|
|
|
return returnVal;
|
|
} catch (UnknownHostException uhe) {
|
|
s_logger.warn("Unable to associate LUN " , uhe);
|
|
throw new ServerException("Unable to associate LUN", uhe);
|
|
}catch (NaAPIFailedException naf){
|
|
if(naf.getErrno() == 9008){ //initiator group already contains node
|
|
return returnVal;
|
|
}
|
|
s_logger.warn("Unable to associate LUN " , naf);
|
|
throw new ServerException("Unable to associate LUN", naf);
|
|
}
|
|
catch (NaException nae) {
|
|
s_logger.warn("Unable to associate LUN " , nae);
|
|
throw new ServerException("Unable to associate LUN", nae);
|
|
} catch (IOException ioe) {
|
|
s_logger.warn("Unable to associate LUN " , ioe);
|
|
throw new ServerException("Unable to associate LUN", ioe);
|
|
}
|
|
finally{
|
|
if (s != null)
|
|
s.close();
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
public boolean configure(String name, Map<String, Object> params)
|
|
throws ConfigurationException {
|
|
|
|
_name = name;
|
|
|
|
_netappAllocator = new NetappDefaultAllocatorImpl( this );
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return _name;
|
|
}
|
|
|
|
@Override
|
|
public boolean start() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean stop() {
|
|
return true;
|
|
}
|
|
|
|
}
|