// 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; } }