Java tutorial
// 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.driver; import java.util.List; import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.*; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.SolidFireUtil; import org.apache.commons.lang.StringUtils; import com.cloud.agent.api.Answer; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.user.AccountVO; import com.cloud.user.AccountDetailsDao; import com.cloud.user.AccountDetailVO; import com.cloud.user.dao.AccountDao; public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Inject private PrimaryDataStoreDao _storagePoolDao; @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; @Inject private VolumeDao _volumeDao; @Inject private VolumeDetailsDao _volumeDetailsDao; @Inject private DataCenterDao _zoneDao; @Inject private AccountDao _accountDao; @Inject private AccountDetailsDao _accountDetailsDao; @Override public DataTO getTO(DataObject data) { return null; } @Override public DataStoreTO getStoreTO(DataStore store) { return null; } private static class SolidFireConnection { private final String _managementVip; private final int _managementPort; private final String _clusterAdminUsername; private final String _clusterAdminPassword; public SolidFireConnection(String managementVip, int managementPort, String clusterAdminUsername, String clusterAdminPassword) { _managementVip = managementVip; _managementPort = managementPort; _clusterAdminUsername = clusterAdminUsername; _clusterAdminPassword = clusterAdminPassword; } public String getManagementVip() { return _managementVip; } public int getManagementPort() { return _managementPort; } public String getClusterAdminUsername() { return _clusterAdminUsername; } public String getClusterAdminPassword() { return _clusterAdminPassword; } } private SolidFireConnection getSolidFireConnection(long storagePoolId) { StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_VIP); String mVip = storagePoolDetail.getValue(); storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_PORT); int mPort = Integer.parseInt(storagePoolDetail.getValue()); storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_USERNAME); String clusterAdminUsername = storagePoolDetail.getValue(); storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_PASSWORD); String clusterAdminPassword = storagePoolDetail.getValue(); return new SolidFireConnection(mVip, mPort, clusterAdminUsername, clusterAdminPassword); } private SolidFireUtil.SolidFireAccount createSolidFireAccount(String sfAccountName, SolidFireConnection sfConnection) { String mVip = sfConnection.getManagementVip(); int mPort = sfConnection.getManagementPort(); String clusterAdminUsername = sfConnection.getClusterAdminUsername(); String clusterAdminPassword = sfConnection.getClusterAdminPassword(); long accountNumber = SolidFireUtil.createSolidFireAccount(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountName); return SolidFireUtil.getSolidFireAccountById(mVip, mPort, clusterAdminUsername, clusterAdminPassword, accountNumber); } private void updateCsDbWithAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount) { AccountDetailVO accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.ACCOUNT_ID, String.valueOf(sfAccount.getId())); _accountDetailsDao.persist(accountDetails); accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_INITIATOR_USERNAME, String.valueOf(sfAccount.getName())); _accountDetailsDao.persist(accountDetails); accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_INITIATOR_SECRET, String.valueOf(sfAccount.getInitiatorSecret())); _accountDetailsDao.persist(accountDetails); accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_TARGET_USERNAME, sfAccount.getName()); _accountDetailsDao.persist(accountDetails); accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_TARGET_SECRET, sfAccount.getTargetSecret()); _accountDetailsDao.persist(accountDetails); } private class ChapInfoImpl implements ChapInfo { private final String _initiatorUsername; private final String _initiatorSecret; private final String _targetUsername; private final String _targetSecret; public ChapInfoImpl(String initiatorUsername, String initiatorSecret, String targetUsername, String targetSecret) { _initiatorUsername = initiatorUsername; _initiatorSecret = initiatorSecret; _targetUsername = targetUsername; _targetSecret = targetSecret; } public String getInitiatorUsername() { return _initiatorUsername; } public String getInitiatorSecret() { return _initiatorSecret; } public String getTargetUsername() { return _targetUsername; } public String getTargetSecret() { return _targetSecret; } } @Override public ChapInfo getChapInfo(VolumeInfo volumeInfo) { long accountId = volumeInfo.getAccountId(); AccountDetailVO accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_INITIATOR_USERNAME); String chapInitiatorUsername = accountDetail.getValue(); accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_INITIATOR_SECRET); String chapInitiatorSecret = accountDetail.getValue(); StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(volumeInfo.getPoolId(), SolidFireUtil.USE_MUTUAL_CHAP_FOR_VMWARE); boolean useMutualChapForVMware = new Boolean(storagePoolDetail.getValue()); String chapTargetUsername = null; String chapTargetSecret = null; if (useMutualChapForVMware) { accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_USERNAME); chapTargetUsername = accountDetail.getValue(); accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_SECRET); chapTargetSecret = accountDetail.getValue(); } return new ChapInfoImpl(chapInitiatorUsername, chapInitiatorSecret, chapTargetUsername, chapTargetSecret); } private long getDefaultMinIops(long storagePoolId) { StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS); String clusterDefaultMinIops = storagePoolDetail.getValue(); return Long.parseLong(clusterDefaultMinIops); } private long getDefaultMaxIops(long storagePoolId) { StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS); String clusterDefaultMaxIops = storagePoolDetail.getValue(); return Long.parseLong(clusterDefaultMaxIops); } private long getDefaultBurstIops(long storagePoolId, long maxIops) { StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS); String clusterDefaultBurstIopsPercentOfMaxIops = storagePoolDetail.getValue(); float fClusterDefaultBurstIopsPercentOfMaxIops = Float.parseFloat(clusterDefaultBurstIopsPercentOfMaxIops); return (long) (maxIops * fClusterDefaultBurstIopsPercentOfMaxIops); } private SolidFireUtil.SolidFireVolume createSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection) { String mVip = sfConnection.getManagementVip(); int mPort = sfConnection.getManagementPort(); String clusterAdminUsername = sfConnection.getClusterAdminUsername(); String clusterAdminPassword = sfConnection.getClusterAdminPassword(); AccountDetailVO accountDetail = _accountDetailsDao.findDetail(volumeInfo.getAccountId(), SolidFireUtil.ACCOUNT_ID); long sfAccountId = Long.parseLong(accountDetail.getValue()); long storagePoolId = volumeInfo.getDataStore().getId(); final Iops iops; Long minIops = volumeInfo.getMinIops(); Long maxIops = volumeInfo.getMaxIops(); if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) { long defaultMaxIops = getDefaultMaxIops(storagePoolId); iops = new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops)); } else { iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops())); } long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeInfo.getSize(), true, iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops()); return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId); } private String getSolidFireVolumeName(String strCloudStackVolumeName) { final String specialChar = "-"; StringBuilder strSolidFireVolumeName = new StringBuilder(); for (int i = 0; i < strCloudStackVolumeName.length(); i++) { String strChar = strCloudStackVolumeName.substring(i, i + 1); if (StringUtils.isAlphanumeric(strChar)) { strSolidFireVolumeName.append(strChar); } else { strSolidFireVolumeName.append(specialChar); } } return strSolidFireVolumeName.toString(); } private static class Iops { private final long _minIops; private final long _maxIops; private final long _burstIops; public Iops(long minIops, long maxIops, long burstIops) throws IllegalArgumentException { if (minIops <= 0 || maxIops <= 0) { throw new IllegalArgumentException("The 'Min IOPS' and 'Max IOPS' values must be greater than 0."); } if (minIops > maxIops) { throw new IllegalArgumentException("The 'Min IOPS' value cannot exceed the 'Max IOPS' value."); } if (maxIops > burstIops) { throw new IllegalArgumentException("The 'Max IOPS' value cannot exceed the 'Burst IOPS' value."); } _minIops = minIops; _maxIops = maxIops; _burstIops = burstIops; } public long getMinIops() { return _minIops; } public long getMaxIops() { return _maxIops; } public long getBurstIops() { return _burstIops; } } private void deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection) { Long storagePoolId = volumeInfo.getPoolId(); if (storagePoolId == null) { return; // this volume was never assigned to a storage pool, so no SAN volume should exist for it } String mVip = sfConnection.getManagementVip(); int mPort = sfConnection.getManagementPort(); String clusterAdminUsername = sfConnection.getClusterAdminUsername(); String clusterAdminPassword = sfConnection.getClusterAdminPassword(); long sfVolumeId = Long.parseLong(volumeInfo.getFolder()); SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId); } private String getSfAccountName(String csAccountUuid, long csAccountId) { return "CloudStack_" + csAccountUuid + "_" + csAccountId; } private boolean sfAccountExists(String sfAccountName, SolidFireConnection sfConnection) { String mVip = sfConnection.getManagementVip(); int mPort = sfConnection.getManagementPort(); String clusterAdminUsername = sfConnection.getClusterAdminUsername(); String clusterAdminPassword = sfConnection.getClusterAdminPassword(); try { SolidFireUtil.getSolidFireAccountByName(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountName); } catch (Exception ex) { return false; } return true; } @Override public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) { String iqn = null; String errMsg = null; if (dataObject.getType() == DataObjectType.VOLUME) { VolumeInfo volumeInfo = (VolumeInfo) dataObject; AccountVO account = _accountDao.findById(volumeInfo.getAccountId()); String sfAccountName = getSfAccountName(account.getUuid(), account.getAccountId()); long storagePoolId = dataStore.getId(); SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId); if (!sfAccountExists(sfAccountName, sfConnection)) { SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName, sfConnection); updateCsDbWithAccountInfo(account.getId(), sfAccount); } SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(volumeInfo, sfConnection); iqn = sfVolume.getIqn(); VolumeVO volume = this._volumeDao.findById(volumeInfo.getId()); volume.set_iScsiName(iqn); volume.setFolder(String.valueOf(sfVolume.getId())); volume.setPoolType(StoragePoolType.IscsiLUN); volume.setPoolId(storagePoolId); _volumeDao.update(volume.getId(), volume); StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId()); long capacityBytes = storagePool.getCapacityBytes(); long usedBytes = storagePool.getUsedBytes(); usedBytes += volumeInfo.getSize(); storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes); _storagePoolDao.update(storagePoolId, storagePool); } else { errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync"; } // path = iqn // size is pulled from DataObject instance, if errMsg is null CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg)); result.setResult(errMsg); callback.complete(result); } private void deleteSolidFireAccount(long sfAccountId, SolidFireConnection sfConnection) { String mVip = sfConnection.getManagementVip(); int mPort = sfConnection.getManagementPort(); String clusterAdminUsername = sfConnection.getClusterAdminUsername(); String clusterAdminPassword = sfConnection.getClusterAdminPassword(); List<SolidFireUtil.SolidFireVolume> sfVolumes = SolidFireUtil.getDeletedVolumes(mVip, mPort, clusterAdminUsername, clusterAdminPassword); // if there are volumes for this account in the trash, delete them (so the account can be deleted) if (sfVolumes != null) { for (SolidFireUtil.SolidFireVolume sfVolume : sfVolumes) { if (sfVolume.getAccountId() == sfAccountId) { SolidFireUtil.purgeSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolume.getId()); } } } SolidFireUtil.deleteSolidFireAccount(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountId); } private boolean sfAccountHasVolume(long sfAccountId, SolidFireConnection sfConnection) { String mVip = sfConnection.getManagementVip(); int mPort = sfConnection.getManagementPort(); String clusterAdminUsername = sfConnection.getClusterAdminUsername(); String clusterAdminPassword = sfConnection.getClusterAdminPassword(); List<SolidFireUtil.SolidFireVolume> sfVolumes = SolidFireUtil.getSolidFireVolumesForAccountId(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountId); if (sfVolumes != null) { for (SolidFireUtil.SolidFireVolume sfVolume : sfVolumes) { if (sfVolume.isActive()) { return true; } } } return false; } @Override public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CommandResult> callback) { String errMsg = null; if (dataObject.getType() == DataObjectType.VOLUME) { VolumeInfo volumeInfo = (VolumeInfo) dataObject; AccountVO account = _accountDao.findById(volumeInfo.getAccountId()); AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID); long sfAccountId = Long.parseLong(accountDetails.getValue()); long storagePoolId = dataStore.getId(); SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId); deleteSolidFireVolume(volumeInfo, sfConnection); _volumeDao.deleteVolumesByInstance(volumeInfo.getId()); // if (!sfAccountHasVolume(sfAccountId, sfConnection)) { // // delete the account from the SolidFire SAN // deleteSolidFireAccount(sfAccountId, sfConnection); // // // delete the info in the account_details table // // that's related to the SolidFire account // _accountDetailsDao.deleteDetails(account.getAccountId()); // } StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); long usedBytes = storagePool.getUsedBytes(); usedBytes -= volumeInfo.getSize(); storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); _storagePoolDao.update(storagePoolId, storagePool); } else { errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync"; } CommandResult result = new CommandResult(); result.setResult(errMsg); callback.complete(result); } @Override public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) { throw new UnsupportedOperationException(); } @Override public boolean canCopy(DataObject srcData, DataObject destData) { return false; } @Override public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) { throw new UnsupportedOperationException(); } @Override public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { throw new UnsupportedOperationException(); } @Override public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) { throw new UnsupportedOperationException(); } }