blob: c1efc75e124ba052d9962b4959bb9d122b50f042 [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.util;
import java.util.HashMap;
import java.util.LinkedList;
import org.apache.cloudstack.storage.datastore.util.NexentaNmsClient.NmsResponse;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import com.cloud.utils.exception.CloudRuntimeException;
import com.google.gson.annotations.SerializedName;
public class NexentaStorAppliance {
private static final Logger logger = LogManager.getLogger(NexentaStorAppliance.class);
protected NexentaNmsClient client;
protected NexentaUtil.NexentaPluginParameters parameters;
public NexentaStorAppliance(NexentaUtil.NexentaPluginParameters parameters) {
client = new NexentaNmsClient(parameters.getNmsUrl());
this.parameters = parameters;
}
NexentaStorAppliance(NexentaNmsClient client, NexentaUtil.NexentaPluginParameters parameters) {
this.client = client;
this.parameters = parameters;
}
String getVolumeName(String volumeName) {
if (volumeName.startsWith("/")) {
return String.format("%s%s", parameters.getVolume(), volumeName);
}
return String.format("%s/%s", parameters.getVolume(), volumeName);
}
static String getTargetName(String volumeName) {
return NexentaUtil.ISCSI_TARGET_NAME_PREFIX + volumeName;
}
static String getTargetGroupName(String volumeName) {
return NexentaUtil.ISCSI_TARGET_GROUP_PREFIX + volumeName;
}
@SuppressWarnings("unused")
static final class IntegerNmsResponse extends NmsResponse {
Integer result;
IntegerNmsResponse(int result) {
this.result = Integer.valueOf(result);
}
public Integer getResult() {
return result;
}
}
@SuppressWarnings("unused")
static final class IscsiTarget {
protected String status;
protected String protocol;
protected String name;
protected String sessions;
protected String alias;
protected String provider;
IscsiTarget(String status, String protocol, String name, String sessions, String alias, String provider) {
this.status = status;
this.protocol = protocol;
this.name = name;
this.sessions = sessions;
this.alias = alias;
this.provider = provider;
}
}
@SuppressWarnings("unused")
static final class ListOfIscsiTargetsNmsResponse extends NmsResponse {
protected HashMap<String, IscsiTarget> result;
ListOfIscsiTargetsNmsResponse() {}
ListOfIscsiTargetsNmsResponse(HashMap<String, IscsiTarget> result) {
this.result = result;
}
public HashMap<String, IscsiTarget> getResult() {
return result;
}
}
/**
* Checks if iSCSI target exists.
* @param targetName iSCSI target name
* @return true if iSCSI target exists, else false
*/
boolean isIscsiTargetExists(String targetName) {
ListOfIscsiTargetsNmsResponse response = (ListOfIscsiTargetsNmsResponse) client.execute(ListOfIscsiTargetsNmsResponse.class, "stmf", "list_targets");
if (response == null) {
return false;
}
HashMap<String, IscsiTarget> result = response.getResult();
return result != null && result.keySet().contains(targetName);
}
@SuppressWarnings("unused")
static final class CreateIscsiTargetRequestParams {
@SerializedName("target_name") String targetName;
CreateIscsiTargetRequestParams(String targetName) {
this.targetName = targetName;
}
@Override
public boolean equals(Object other) {
return other instanceof CreateIscsiTargetRequestParams && targetName.equals(((CreateIscsiTargetRequestParams) other).targetName);
}
}
/**
* Creates iSCSI target on NexentaStor Appliance.
* @param targetName iSCSI target name
*/
void createIscsiTarget(String targetName) {
try {
client.execute(NmsResponse.class, "iscsitarget", "create_target", new CreateIscsiTargetRequestParams(targetName));
} catch (CloudRuntimeException ex) {
if (!ex.getMessage().contains("already configured")) {
throw ex;
}
logger.debug("Ignored target creation error: " + ex);
}
}
@SuppressWarnings("unused")
static final class ListOfStringsNmsResponse extends NmsResponse {
LinkedList<String> result;
ListOfStringsNmsResponse() {}
ListOfStringsNmsResponse(LinkedList<String> result) {
this.result = result;
}
public LinkedList<String> getResult() {
return result;
}
}
/**
* Checks if iSCSI target group already exists on NexentaStor Appliance.
* @param targetGroupName iSCSI target group name
* @return true if iSCSI target group already exists, else false
*/
boolean isIscsiTargetGroupExists(String targetGroupName) {
ListOfStringsNmsResponse response = (ListOfStringsNmsResponse) client.execute(ListOfStringsNmsResponse.class, "stmf", "list_targetgroups");
if (response == null) {
return false;
}
LinkedList<String> result = response.getResult();
return result != null && result.contains(targetGroupName);
}
/**
* Creates iSCSI target group on NexentaStor Appliance.
* @param targetGroupName iSCSI target group name
*/
void createIscsiTargetGroup(String targetGroupName) {
try {
client.execute(NmsResponse.class, "stmf", "create_targetgroup", targetGroupName);
} catch (CloudRuntimeException ex) {
if (!ex.getMessage().contains("already exists") && !ex.getMessage().contains("target must be offline")) {
throw ex;
}
logger.info("Ignored target group creation error: " + ex);
}
}
/**
* Checks if iSCSI target is member of target group.
* @param targetGroupName iSCSI target group name
* @param targetName iSCSI target name
* @return true if target is member of iSCSI target group, else false
*/
boolean isTargetMemberOfTargetGroup(String targetGroupName, String targetName) {
ListOfStringsNmsResponse response = (ListOfStringsNmsResponse) client.execute(ListOfStringsNmsResponse.class, "stmf", "list_targetgroup_members", targetGroupName);
if (response == null) {
return false;
}
LinkedList<String> result = response.getResult();
return result != null && result.contains(targetName);
}
/**
* Adds iSCSI target to target group.
* @param targetGroupName iSCSI target group name
* @param targetName iSCSI target name
*/
void addTargetGroupMember(String targetGroupName, String targetName) {
try {
client.execute(NmsResponse.class, "stmf", "add_targetgroup_member", targetGroupName, targetName);
} catch (CloudRuntimeException ex) {
if (!ex.getMessage().contains("already exists") && !ex.getMessage().contains("target must be offline")) {
throw ex;
}
logger.debug("Ignored target group member addition error: " + ex);
}
}
/**
* Checks if LU already exists on NexentaStor appliance.
* @param luName LU name
* @return true if LU already exists, else false
*/
boolean isLuExists(String luName) {
IntegerNmsResponse response;
try {
response = (IntegerNmsResponse) client.execute(IntegerNmsResponse.class, "scsidisk", "lu_exists", luName);
} catch (CloudRuntimeException ex) {
if (ex.getMessage().contains("does not exist")) {
return false;
}
throw ex;
}
return response!= null && response.getResult() > 0;
}
@SuppressWarnings("unused")
static final class LuParams {
@Override
public boolean equals(Object other) {
return other instanceof LuParams;
}
}
/**
* Creates LU for volume.
* @param volumeName volume name
*/
void createLu(String volumeName) {
try {
client.execute(NmsResponse.class, "scsidisk", "create_lu", volumeName, new LuParams());
} catch (CloudRuntimeException ex) {
if (!ex.getMessage().contains("in use")) {
throw ex;
}
logger.info("Ignored LU creation error: " + ex);
}
}
/**
* Checks if LU shared on NexentaStor appliance.
* @param luName LU name
* @return true if LU was already shared, else false
*/
boolean isLuShared(String luName) {
IntegerNmsResponse response;
try {
response = (IntegerNmsResponse) client.execute(IntegerNmsResponse.class, "scsidisk", "lu_shared", luName);
} catch (CloudRuntimeException ex) {
if (ex.getMessage().contains("does not exist")) {
return false;
}
throw ex;
}
return response != null && response.getResult() > 0;
}
@SuppressWarnings("unused")
static final class MappingEntry {
@SerializedName("target_group") String targetGroup;
String lun;
String zvol;
@SerializedName("host_group") String hostGroup;
@SerializedName("entry_number") String entryNumber;
MappingEntry(String targetGroup, String lun) {
this.targetGroup = targetGroup;
this.lun = lun;
}
static boolean isEquals(Object a, Object b) {
return (a == null && b == null) || (a != null && a.equals(b));
}
@Override
public boolean equals(Object other) {
if (other instanceof MappingEntry) {
MappingEntry o = (MappingEntry) other;
return isEquals(targetGroup, o.targetGroup) && isEquals(lun, o.lun) && isEquals(zvol, o.zvol) &&
isEquals(hostGroup, o.hostGroup) && isEquals(entryNumber, o.entryNumber);
}
return false;
}
}
@SuppressWarnings("unused")
static final class AddMappingEntryNmsResponse extends NmsResponse {
MappingEntry result;
}
/**
* Adds LU mapping entry to iSCSI target group.
* @param luName LU name
* @param targetGroupName iSCSI target group name
*/
void addLuMappingEntry(String luName, String targetGroupName) {
MappingEntry mappingEntry = new MappingEntry(targetGroupName, "0");
try {
client.execute(AddMappingEntryNmsResponse.class, "scsidisk", "add_lun_mapping_entry", luName, mappingEntry);
} catch (CloudRuntimeException ex) {
if (!ex.getMessage().contains("view already exists")) {
throw ex;
}
logger.debug("Ignored LU mapping entry addition error " + ex);
}
}
NexentaStorZvol createIscsiVolume(String volumeName, Long volumeSize) {
final String zvolName = getVolumeName(volumeName);
String volumeSizeString = String.format("%dB", volumeSize);
client.execute(NmsResponse.class, "zvol", "create", zvolName, volumeSizeString, parameters.getVolumeBlockSize(), parameters.getSparseVolumes());
final String targetName = getTargetName(volumeName);
final String targetGroupName = getTargetGroupName(volumeName);
if (!isIscsiTargetExists(targetName)) {
createIscsiTarget(targetName);
}
if (!isIscsiTargetGroupExists(targetGroupName)) {
createIscsiTargetGroup(targetGroupName);
}
if (!isTargetMemberOfTargetGroup(targetGroupName, targetName)) {
addTargetGroupMember(targetGroupName, targetName);
}
if (!isLuExists(zvolName)) {
createLu(zvolName);
}
if (!isLuShared(zvolName)) {
addLuMappingEntry(zvolName, targetGroupName);
}
return new NexentaStorZvol(zvolName, targetName);
}
static abstract class NexentaStorVolume {
protected String name;
NexentaStorVolume(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
}
public static final class NexentaStorZvol extends NexentaStorVolume {
protected String iqn;
public NexentaStorZvol(String name, String iqn) {
super(name);
this.iqn = iqn;
}
public String getIqn() {
return iqn;
}
}
public void deleteIscsiVolume(String volumeName) {
try {
NmsResponse response = client.execute(NmsResponse.class, "zvol", "destroy", volumeName, "");
} catch (CloudRuntimeException ex) {
if (!ex.getMessage().contains("does not exist")) {
throw ex;
}
logger.debug(String.format(
"Volume %s does not exist, it seems it was already " +
"deleted.", volumeName));
}
}
public NexentaStorVolume createVolume(String volumeName, Long volumeSize) {
return createIscsiVolume(volumeName, volumeSize);
}
public void deleteVolume(String volumeName) {
deleteIscsiVolume(volumeName);
}
}