/*
* Copyright (c) 2008-2011 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.isilon.restapi;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.ConnectException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.MultivaluedMap;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.isilon.restapi.IsilonOneFS8Event.Events;
import com.emc.storageos.services.util.SecurityUtils;
import com.google.gson.Gson;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.ClientResponse.Status;
import com.sun.jersey.core.util.MultivaluedMapImpl;
/*
* Isilon REST Api
*
* NOTE: We call close() on every IsilonAPI response to make sure that http connections are released in all cases.
* Jersey releases http connections automatically in two cases: if response does not have entity and when entity was read from response.
* In case when response has entity and entity was not read , Jersey does not release connection
* (see JavaDoc for Jersey's ApacheHttpClientHandler.java) and connection is not returned back to connection pool to be available for
* new request. We do call close on response every time (sometimes unnecessary) to make sure we do not miss any case.
*/
public class IsilonApi {
private final URI _baseUrl;
private final RESTClient _client;
private static final int directorySoftQuotaDefaultSize = 95;
private static final long directorySoftQuotaDefaultGracePeriod = 7L;
private static final int directoryAdvisoryQuotaSize = 85;
private static final URI URI_IFS = URI.create("/namespace/");
private static final URI URI_ALIAS = URI.create("/platform/1/protocols/nfs/aliases/");
private static final URI URI_NFS_EXPORTS = URI.create("/platform/1/protocols/nfs/exports/");
private static final URI URI_SMB_SHARES = URI.create("/platform/1/protocols/smb/shares/");
private static final URI URI_SNAPSHOTS = URI.create("/platform/1/snapshot/snapshots/");
private static final URI URI_QUOTAS = URI.create("/platform/1/quota/quotas/");
private static final URI URI_CLUSTER = URI.create("/platform/1/cluster/identity");
private static final URI URI_CLUSTER_CONFIG = URI.create("/platform/1/cluster/config");
private static final URI URI_STATS = URI.create("/platform/1/statistics/");
private static final URI URI_STORAGE_POOLS = URI.create("/platform/1/storagepool/storagepools");
private static final URI URI_DISK_POOLS = URI.create("/platform/1/diskpool/diskpools");
private static final URI URI_ARRAY_GLOBAL_STATUS = URI.create("/platform/1/protocols/nfs/settings/global");
private static final URI URI_ARRAY_GLOBAL_STATUS_ONEFS8 = URI.create("/platform/3/protocols/nfs/settings/global");
private static final URI URI_STORAGE_PORTS = URI
.create("/platform/1/cluster/smartconnect_zones");
// private static final URI URI_EVENTS = URI.create("/platform/1/events/");
private static final URI URI_EVENTS = URI.create("/platform/2/event/events/");
private static final URI URI_ONEFS8_EVENTS = URI.create("/platform/3/event/eventlists/");
private static final URI URI_ACCESS_ZONES = URI.create("/platform/1/zones");
private static final URI URI_NETWORK_POOLS = URI.create("/platform/3/network/pools");
private static final URI URI_SYNCIQ_SERVICE_STATUS = URI.create("/platform/1/sync/settings");
private static final URI URI_REPLICATION_LICENSE_INFO = URI.create("/platform/1/sync/license");
private static final URI URI_REPLICATION_POLICIES = URI.create("/platform/1/sync/policies/");
private static final URI URI_REPLICATION_POLICIES_8 = URI.create("/platform/3/sync/policies/");
private static final URI URI_REPLICATION_JOBS = URI.create("/platform/1/sync/jobs");
private static final URI URI_REPLICATION_JOB = URI.create("/platform/1/sync/jobs/");
private static final URI URI_TARGET_REPLICATION_POLICIES = URI.create("platform/1/sync/target/policies/");
private static final URI URI_REPLICATION_POLICY_REPORTS = URI.create("/platform/1/sync/reports?policy_name=");
private static final URI URI_TARGET_REPLICATION_POLICY_REPORTS = URI.create("/platform/1/sync/target/reports?policy_name=");
private static final URI URI_SNAPSHOTIQ_LICENSE_INFO = URI.create("/platform/1/snapshot/license");
private static final URI URI_SNAPSHOT_SCHEDULES = URI.create("/platform/1/snapshot/schedules/");
private static Logger sLogger = LoggerFactory.getLogger(IsilonApi.class);
private static final URI URI_SMARTQUOTA_LICENSE_INFO = URI.create("/platform/1/quota/license");
public enum IsilonLicenseType {
SMARTQUOTA, SNAPSHOT
}
private static final Map<IsilonLicenseType, URI> licenseMap;
static {
Map<IsilonLicenseType, URI> result = new HashMap<IsilonLicenseType, URI>();
result.put(IsilonLicenseType.SMARTQUOTA, URI_SMARTQUOTA_LICENSE_INFO);
result.put(IsilonLicenseType.SMARTQUOTA, URI_SNAPSHOTIQ_LICENSE_INFO);
licenseMap = Collections.unmodifiableMap(result);
}
/**
* Class representing Isilon list API return value
*
* @param <T>
* type of object in the list
*/
public static class IsilonList<T> {
// list of objects returned
private final ArrayList<T> _list;
// resume token for longer lists
private String _token;
public IsilonList() {
_list = new ArrayList<T>();
_token = null;
}
public void add(T obj) {
_list.add(obj);
}
public void addList(List<T> objList) {
_list.addAll(objList);
}
public int size() {
return _list.size();
}
public ArrayList<T> getList() {
return _list;
}
public void setToken(String token) {
_token = token;
}
public String getToken() {
return _token;
}
}
public IsilonApi(URI endpoint, RESTClient client) {
_baseUrl = endpoint;
_client = client;
}
/**
* Close client resources
*/
public void close() {
_client.close();
}
/**
* Get cluster info from the isilon array
*
* @return IsilonClusterInfo object
* @throws IsilonException
*/
public IsilonClusterInfo getClusterInfo() throws IsilonException {
ClientResponse clientResp = null;
clientResp = _client.get(_baseUrl.resolve(URI_CLUSTER));
if (clientResp.getStatus() != 200) {
throw IsilonException.exceptions.unableToConnect(_baseUrl);
}
try {
JSONObject resp = clientResp.getEntity(JSONObject.class);
IsilonClusterInfo info = new Gson().fromJson(SecurityUtils.sanitizeJsonString(resp.toString()), IsilonClusterInfo.class);
// explicitly parse onefs-version, since the key name has "-", we
// can not do it directly.
// TODO: new PAPI does not have ""onefs-version-info" element in the
// response...
// TODO: version data is part of ../cluster/config response as
// "onefs_version", see new cluster API
// info.setVersion(resp.getString("onefs-version-info"));
return info;
} catch (Exception e) {
throw IsilonException.exceptions.unableToGetIsilonClusterInfo(e.getMessage(), e);
} finally {
if (clientResp != null) {
clientResp.close();
}
}
}
/**
* Get cluster configuration from the isilon array
*
* @return IsilonClusterConfig object
* @throws IsilonException
*/
public IsilonClusterConfig getClusterConfig() throws IsilonException {
ClientResponse clientResp = null;
try {
clientResp = _client.get(_baseUrl.resolve(URI_CLUSTER_CONFIG));
if (clientResp.getStatus() != 200) {
throw IsilonException.exceptions.unableToGetIsilonClusterConfig(clientResp.getStatus());
}
JSONObject resp = clientResp.getEntity(JSONObject.class);
IsilonClusterConfig config = new Gson().fromJson(SecurityUtils.sanitizeJsonString(resp.toString()),
IsilonClusterConfig.class);
return config;
} catch (Exception e) {
String msg = String.format("%1$s", (clientResp == null) ? "" : clientResp);
throw IsilonException.exceptions.unableToGetIsilonClusterConfig(msg, e);
} finally {
if (clientResp != null) {
clientResp.close();
}
}
}
/**
*
* /**
* Get list of all sub directories of fspath
*
* @param fspath
* directory path to lookup
* @return ArrayList<String> list of names of sub directories
* @throws IsilonException
*/
public IsilonList<String> listDir(String fspath, String resumeToken) throws IsilonException {
fspath = scrubPath(fspath);
ClientResponse clientResp = null;
try {
fspath = URLEncoder.encode(fspath, "UTF-8");
IsilonList<String> ret = new IsilonList<String>();
String query = (resumeToken == null) ? "?type=container" : "?type=container&resume=" + resumeToken;
clientResp = _client.get(_baseUrl.resolve(URI_IFS.resolve(fspath + query)));
if (clientResp.getStatus() != 200) {
processErrorResponse("list", "directories", clientResp.getStatus(),
clientResp.getEntity(JSONObject.class));
} else {
JSONObject resp = clientResp.getEntity(JSONObject.class);
sLogger.debug("listDir: Output from Server {}", resp.get("children"));
JSONArray ar = (JSONArray) resp.get("children");
for (int i = 0; i < ar.length(); i++) {
JSONObject ind = ar.getJSONObject(i);
ret.add(ind.get("name").toString());
}
if (resp.has("resume") && !resp.getString("resume").equals("null")) {
// we have more records to fetch -- save the resume token
ret.setToken(resp.getString("resume"));
}
}
return ret;
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String msg = String.format("%1$s", (clientResp == null) ? "" : clientResp);
throw IsilonException.exceptions.unableToGetSubDirectoryList(msg, e);
} finally {
if (clientResp != null) {
clientResp.close();
}
}
}
/**
* Checks to see if the dir with the given path exists on the isilon device
*
* @param fspath
* directory path to chek
* @return boolean true if exists, false otherwise
*/
public boolean existsDir(String fspath) throws IsilonException {
fspath = scrubPath(fspath);
ClientResponse resp = null;
try {
fspath = URLEncoder.encode(fspath, "UTF-8");
sLogger.debug("IsilonApi existsDir {} - start", fspath);
resp = _client.head(_baseUrl.resolve(URI_IFS.resolve(fspath)));
sLogger.debug("IsilonApi existsDir {} - complete", fspath);
if (resp.getStatus() != 200) {
return false;
}
return true;
} catch (Exception e) {
if (e.getCause() instanceof ConnectException) {
throw IsilonException.exceptions.unableToConnect(_baseUrl, e);
}
final Status status = resp != null ? resp.getClientResponseStatus() : Status.NOT_FOUND;
throw IsilonException.exceptions.existsDirFailed(fspath, status, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Create a directory with the path specified, will fail if parent does not
* exist
*
* @param fspath
* Dir path to be created
* @throws IsilonException
*/
public void createDir(String fspath) throws IsilonException {
createDir(fspath, false);
}
/**
* Create a directory with the path specified
*
* @param fspath
* Dir path to be created
* @param recursive
* if true, will create parent recursively if it doesn't
* exist
* @throws IsilonException
*/
public void createDir(String fspath, boolean recursive) throws IsilonException {
fspath = scrubPath(fspath);
ClientResponse resp = null;
try {
// check if already exists
if (existsDir(fspath)) {
return;
}
fspath = URLEncoder.encode(fspath, "UTF-8");
MultivaluedMap<String, String> queryParams = null;
if (recursive) {
queryParams = new MultivaluedMapImpl();
queryParams.add("recursive", "1");
}
sLogger.debug("IsilonApi createDir {} - start", fspath);
resp = _client.put(_baseUrl.resolve(URI_IFS.resolve(fspath)), queryParams, "");
sLogger.debug("IsilonApi createDir {} - complete", fspath);
if (resp.getStatus() != 200) {
processErrorResponse("create directory", fspath, resp.getStatus(),
resp.getEntity(JSONObject.class));
}
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
if (e.getCause() instanceof ConnectException) {
throw IsilonException.exceptions.unableToConnect(_baseUrl, e);
}
final Status status = resp != null ? resp.getClientResponseStatus() : Status.NOT_FOUND;
throw IsilonException.exceptions.createDirFailed(fspath, status, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Delete directory on isilon, will fail if any sub directories exist
*
* @param fspath
* directory path
* @throws IsilonException
*/
public void deleteDir(String fspath) throws IsilonException {
deleteDir(fspath, false);
}
/**
* Delete directory on isilon
*
* @param fspath
* directory path
* @param recursive
* if true, will delete all sub directories also
* @throws IsilonException
*/
public void deleteDir(String fspath, boolean recursive) throws IsilonException {
fspath = scrubPath(fspath);
ClientResponse resp = null;
try {
fspath = URLEncoder.encode(fspath, "UTF-8");
resp = _client.delete(_baseUrl.resolve(URI_IFS.resolve(fspath
+ (recursive ? "?recursive=1" : ""))));
if (resp.getStatus() != 200 && resp.getStatus() != 204 && resp.getStatus() != 404) {
processErrorResponse("delete", "directory: " + fspath, resp.getStatus(),
resp.hasEntity() ? resp.getEntity(JSONObject.class) : null);
}
} catch (Exception e) {
throw IsilonException.exceptions.deleteDirFailedOnIsilonArray(e.getMessage(), e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Generic list resources implementation
*
* @param url
* url to get from
* @param key
* key representing the array in the response, also represents
* the type of object to be listed
* @param c
* Class of the object to parse from the list
* @return IsilonList<T> ArrayList of objects parsed
* @throws IsilonException
*/
private <T> IsilonList<T> list(URI url, String key, Class<T> c, String resumeToken)
throws IsilonException {
ClientResponse resp = null;
try {
URI getUrl = url;
if (resumeToken != null && !resumeToken.isEmpty()) {
// we have a resume token, add it to the url
getUrl = getUrl.resolve("?resume=" + resumeToken);
}
resp = _client.get(getUrl);
JSONObject obj = resp.getEntity(JSONObject.class);
IsilonList<T> ret = new IsilonList<T>();
if (resp.getStatus() == 200) {
sLogger.debug("list {} : Output from Server: {} ", key, obj.get(key).toString());
// TODO: "total" is not supported in all lists in Isilon API
// build 354. List of events and quotas do not have "total".
// TODO: Need to clarify with Isilon why "total" was droped from
// some lists and put this code back when fixed if this is
// Isilon bug.
// String count = obj.getString("total");
JSONArray array = obj.getJSONArray(key);
for (int i = 0; i < array.length(); i++) {
JSONObject exp = array.getJSONObject(i);
ret.add(new Gson().fromJson(SecurityUtils.sanitizeJsonString(exp.toString()), c));
}
// Isilon PAPI sets "total" to "null" string when there are more
// entries than default page size (1000 entries). Saw this for
// "events".
// TODO: count is not support in all lists in b. 354, see note
// above
// if (count.equals("null") || Integer.parseInt(count) !=
// ret.size()) {
if (obj.has("resume") && !obj.getString("resume").equals("null")) {
// we have more records to fetch -- save the resume token
ret.setToken(obj.getString("resume"));
}
// }
} else {
processErrorResponse("list", key, resp.getStatus(), obj);
}
return ret;
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.listResourcesFailedOnIsilonArray(key, response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Generic create resource implementation
*
* @param url
* url to post the create to
* @param key
* reference string used in error reporting, representing the
* object type
* @param obj
* Object to post for the create
* @return String identifier returns from the server
* @throws IsilonException
*/
private <T> String create(URI url, String key, T obj) throws IsilonException {
ClientResponse resp = null;
try {
String body = new Gson().toJson(obj);
String id = null;
resp = _client.post(url, body);
if (resp.hasEntity()) {
JSONObject jObj = resp.getEntity(JSONObject.class);
// JSONObject jObj = checkStatusAndParseObject(resp, "create " +
// key + ": failed: ");
sLogger.debug("create {} : Output from Server : ", key, jObj.toString());
if (jObj.has("id")) {
id = jObj.getString("id");
} else {
processErrorResponse("create", key, resp.getStatus(), jObj);
}
} else {
// no entity
processErrorResponse("create", key, resp.getStatus(), null);
}
return id;
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.createResourceFailedOnIsilonArray(key, response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Generic delete resource
*
* @param url
* url to delete
* @param id
* identifier to be deleted
* @param key
* reference string representing the object type being deleted
* @throws IsilonException
*/
private void delete(URI url, String id, String key) throws IsilonException {
ClientResponse resp = null;
try {
resp = _client.delete(url.resolve(id));
if (resp.getStatus() != 200 && resp.getStatus() != 204 && resp.getStatus() != 404) {
processErrorResponse("delete", key + ": " + id, resp.getStatus(),
resp.hasEntity() ? resp.getEntity(JSONObject.class) : null);
// checkStatusAndParseObject(resp,
// String.format("delete %1$s failed for id: %2$s", key , id));
}
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.deleteResourceFailedOnIsilonArray(key, id, response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Create snapshot Schedule implementation
*
* @param url
* url to post the create to
* @param key
* reference string used in error reporting, representing the
* object type
* @param obj
* Object to post for the create
* @return String identifier returns from the server
* @throws IsilonException
*/
private <T> String createSnapshotSchedule(URI url, String key, T obj) throws IsilonException {
ClientResponse resp = null;
try {
String body = new Gson().toJson(obj);
String id = null;
resp = _client.post(url, body);
if (resp.hasEntity()) {
JSONObject jObj = resp.getEntity(JSONObject.class);
sLogger.debug("Create Snapshot Scedule {} : Output from Server : ", key, jObj.toString());
if (jObj.has("id")) {
id = jObj.getString("id");
} else {
throw IsilonException.exceptions.createSnapshotScheduleError(key, jObj.toString());
}
} else {
// no entity
throw IsilonException.exceptions.createSnapshotScheduleError(key, String.valueOf(resp.getStatus()));
}
return id;
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.createResourceFailedOnIsilonArray(key, response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* delete the snapshot schedule
*
* @param url
* url to delete
* @param id
* identifier to be deleted
* @param key
* reference string representing the object type being deleted
* @throws IsilonException
*/
private void deleteSnapshotSchedule(URI url) throws IsilonException {
ClientResponse resp = null;
try {
resp = _client.delete(url);
// error 404 means Snapshot Schedule can not be found, assuming it already deleted.
if (resp.getStatus() != 200 && resp.getStatus() != 204 && resp.getStatus() != 404) {
processErrorResponse("delete", "URL =" + url, resp.getStatus(),
resp.hasEntity() ? resp.getEntity(JSONObject.class) : null);
}
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.deletePolicyFailedOnIsilonArray(url.toString(), response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/* snapshot schedule */
/**
* Create snapshot schedule
*
* @param name
* String label to be used for the snapshot schedule
* @param path
* directory path to snapshot
* @param schedule
* frequency at which snapshot is taken
* @param pattern
* naming pattern for the snapshot
* @param duration
* expiration of snapshot
* @return String identifier for the snapshot schedule created
* @throws IsilonException
*/
public String createSnapshotSchedule(String name, String path, String schedule, String pattern, Integer duration)
throws IsilonException {
IsilonSnapshotSchedule isiSchedule = new IsilonSnapshotSchedule(name, path, schedule, pattern, duration);
sLogger.info("Isilon snapshot schedule: {} creation started", isiSchedule.toString());
return createSnapshotSchedule(isiSchedule);
}
/**
* Create snapshot schedule
*
* @param isiSchedule
* @return String identifier for the snapshot schedule created
* @throws IsilonException
*/
public String createSnapshotSchedule(IsilonSnapshotSchedule isiSchedule)
throws IsilonException {
sLogger.info("Isilon snapshot schedule: {} creation started", isiSchedule.toString());
return createSnapshotSchedule(_baseUrl.resolve(URI_SNAPSHOT_SCHEDULES), "schedule", isiSchedule);
}
/**
* Modify snapshot schedule
*
* @param id
* Identifier for the snapshot schedule to be modified
* @param s
* schedules object with the modified values
* @throws IsilonException
*/
public void modifySnapshotSchedule(String id, IsilonSnapshotSchedule s) throws IsilonException {
try {
id = URLEncoder.encode(id, "UTF-8");
} catch (UnsupportedEncodingException e) {
sLogger.error("UnsupportedEncodingException occured", e);
}
modify(_baseUrl.resolve(URI_SNAPSHOT_SCHEDULES), id, "schedule", s);
}
/**
* Delete a snapshot schedule
*
* @param id
* Identifier of the snapshot to delete
* @throws IsilonException
*/
public void deleteSnapshotSchedule(String id) throws IsilonException {
try {
id = URLEncoder.encode(id, "UTF-8");
} catch (UnsupportedEncodingException e) {
sLogger.error("UnsupportedEncodingException occured", e);
}
deleteSnapshotSchedule(_baseUrl.resolve(URI_SNAPSHOT_SCHEDULES + "/" + id));
}
/**
*
* @return
* @throws IsilonException
*/
public IsilonList<IsilonSnapshotSchedule> getSnapshotSchedules() throws IsilonException {
return list(_baseUrl.resolve(URI_SNAPSHOT_SCHEDULES), "schedules", IsilonSnapshotSchedule.class, "");
}
/**
* Generic get resource
*
* @param url
* url to get from
* @param id
* identifier for the object
* @param key
* reference string representing the object type being deleted
* @param c
* Class of object representing the return value
* @return T Object parsed from the response, on success
* @throws IsilonException
*/
private <T> T get(URI url, String id, String key, Class<T> c) throws IsilonException {
ClientResponse resp = null;
try {
T returnInstance = null;
resp = _client.get(url.resolve(id));
if (resp.hasEntity()) {
JSONObject jObj = resp.getEntity(JSONObject.class);
if (resp.getStatus() == 200) {
JSONArray array = jObj.getJSONArray(key);
if (array.length() != 1) {
String length = String.format("%1$s", array.length());
throw IsilonException.exceptions.getResourceFailedOnIsilonArray(key, length);
}
JSONObject exp = array.getJSONObject(0);
returnInstance = new Gson().fromJson(SecurityUtils.sanitizeJsonString(exp.toString()), c);
} else {
processErrorResponse("get", key + ": " + id, resp.getStatus(), jObj);
}
} else {
// no entity in response
processErrorResponse("get", key + ": " + id, resp.getStatus(), null);
}
return returnInstance;
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.getResourceFailedOnIsilonArrayExc(key, id, response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Generic get resource when key is not applicable
*
* @param url
* url to get from
* @param id
* identifier for the object
* @param c
* Class of object representing the return value
* @return T Object parsed from the response, on success
* @throws IsilonException
*/
private <T> T getObj(URI url, String id, Class<T> c) throws IsilonException {
ClientResponse resp = null;
try {
T returnInstance = null;
resp = _client.get(url.resolve(id));
if (resp.hasEntity()) {
JSONObject jObj = resp.getEntity(JSONObject.class);
if (resp.getStatus() == 200) {
returnInstance = new Gson().fromJson(jObj.toString(), c);
} else {
processErrorResponse("get", id, resp.getStatus(), jObj);
}
} else {
// no entity in response
processErrorResponse("get", id, resp.getStatus(), null);
}
return returnInstance;
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.getResourceFailedOnIsilonArrayExc("", id, response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Generic modify resource with 204 as HTTP response code.
*
* @param url
* url to PUT the modify request
* @param id
* identifier for the object to modify
* @param key
* object type represented as string for error reporting
* @param obj
* modified object to put
* @throws IsilonException
*/
private <T> void modify(URI url, String id, String key, T obj) throws IsilonException {
ClientResponse resp = null;
try {
String body = new Gson().toJson(obj);
resp = _client.put(url.resolve(id), null, body);
if (resp.getStatus() != 204) {
// error
if (resp.hasEntity()) {
JSONObject jObj = resp.getEntity(JSONObject.class);
processErrorResponse("modify", key + ": " + id, resp.getStatus(), jObj);
} else {
// no entity
processErrorResponse("modify", key + ": " + id, resp.getStatus(), null);
}
}
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.modifyResourceFailedOnIsilonArray(key, id,
response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/**
* Generic modify resource with 200 as HTTP response code.
*
* @param url
* url to PUT the modify request
* @param id
* identifier for the object to modify
* @param key
* object type represented as string for error reporting
* @param obj
* modified object to put
* @throws IsilonException
*/
private <T> void put(URI url, String id, String key, T obj) throws IsilonException {
ClientResponse resp = null;
try {
String body = new Gson().toJson(obj);
resp = _client.put(url.resolve(id), null, body);
if (resp.getStatus() != 200) {
// error
if (resp.hasEntity()) {
JSONObject jObj = resp.getEntity(JSONObject.class);
processErrorResponse("modify", key + ": " + id, resp.getStatus(), jObj);
} else {
// no entity
processErrorResponse("modify", key + ": " + id, resp.getStatus(), null);
}
}
} catch (IsilonException ie) {
throw ie;
} catch (Exception e) {
String response = String.format("%1$s", (resp == null) ? "" : resp);
throw IsilonException.exceptions.modifyResourceFailedOnIsilonArray(key, id,
response, e);
} finally {
if (resp != null) {
resp.close();
}
}
}
/* Aliases */
/*
* Not working on isilon yet -- commenting for now public String
* createAlias(String fspath, String alias) throws IsilonException {
* JSONObject obj; try { obj = new JSONObject(); obj.append("name", alias);
* obj.append("path", fspath); } catch (Exception ex) { throw new
* IsilonException("create alias failed alias: " + alias +
* ": got exception: " + ex.getMessage()); } return create(_baseUrl +
* URI_ALIAS, "alias", obj);
*
* }
*
* public void deleteAlias(String alias) throws IsilonException {
* delete(_baseUrl+ URI_ALIAS, alias, "alias"); }
*/
/* Exports */
/**
* List all exports
*
* @return IsilonList of IsilonExport objects
* @throws IsilonException
*/
public IsilonList<IsilonExport> listExports(String resumeToken) throws IsilonException {
return list(_baseUrl.resolve(URI_NFS_EXPORTS), "exports", IsilonExport.class, resumeToken);
}
/**
* List all exports for given access zone
*
* @return IsilonList of IsilonExport objects
* @throws IsilonException
*/
public IsilonList<IsilonExport> listExports(String resumeToken, String zoneName) throws IsilonException {
URI uri = URI_NFS_EXPORTS;
if (zoneName != null) {
String baseUrl = getURIWithZoneName(_baseUrl.resolve(uri).toString(), zoneName);
uri = URI.create(baseUrl);
sLogger.info("get list of nfs exports for accesszone {} and uri {} ", zoneName, uri.toString());
} else {
uri = _baseUrl.resolve(uri);
}
return list(uri, "exports", IsilonExport.class, resumeToken);
}
/**
* Create export
*
* @param exp
* IsilonExport object with paths and clients set
* @param force boolean flag to ignore client FQDN check against DNS
* @return String identifier for the export created
* @throws IsilonException
*/
public String createExport(IsilonExport exp, boolean force) throws IsilonException {
if (force) {
return create(_baseUrl.resolve(URI_NFS_EXPORTS + "?force=true"), "Export", exp);
} else {
return create(_baseUrl.resolve(URI_NFS_EXPORTS), "Export", exp);
}
}
/**
* Create export on access zone
*
* @param exp
* IsilonExport object with paths and clients set
* @param force boolean flag to ignore client FQDN check against DNS
* @return String identifier for the export created
* @throws IsilonException
*/
public String createExport(IsilonExport exp, String zoneName, boolean force) throws IsilonException {
String baseUrl = getURIWithZoneName(_baseUrl.resolve(URI_NFS_EXPORTS).toString(), zoneName);
URI uri = URI.create(baseUrl);
if (force) {
uri = URI.create(baseUrl + "&force=true");
}
return create(uri, "Export", exp);
}
/**
* Modify export
*
* @param id
* identifier of the export to modify
* @param exp
* IsilonExport object with the modified properties
* @param force boolean flag to ignore client FQDN check against DNS
* @throws IsilonException
*/
public void modifyExport(String id, IsilonExport exp, boolean force) throws IsilonException {
if (force) {
id = id + "?force=true";
}
modify(_baseUrl.resolve(URI_NFS_EXPORTS), id, "export", exp);
}
/**
* Modify export in access zone
*
* @param id
* identifier of the export to modify
* @param exp
* IsilonExport object with the modified properties
* @param force boolean flag to ignore client FQDN check against DNS
* @throws IsilonException
*/
public void modifyExport(String id, String zoneName, IsilonExport exp, boolean force) throws IsilonException {
String uriWithZoneName = getURIWithZoneName(id, zoneName);
if (force) {
uriWithZoneName = uriWithZoneName + "&force=true";
}
modify(_baseUrl.resolve(URI_NFS_EXPORTS), uriWithZoneName, "export", exp);
}
/**
* Get export
*
* @param id
* identifier of the export to get
* @return IsilonExport object
* @throws IsilonException
*/
public IsilonExport getExport(String id) throws IsilonException {
return get(_baseUrl.resolve(URI_NFS_EXPORTS), id, "exports", IsilonExport.class);
}
/**
* Get export for given access zone
*
* @param id
* identifier of the export to get
* @return IsilonExport object
* @throws IsilonException
*/
public IsilonExport getExport(String id, String zoneName) throws IsilonException {
String uriWithZoneName = getURIWithZoneName(id, zoneName);
return get(_baseUrl.resolve(URI_NFS_EXPORTS), uriWithZoneName, "exports", IsilonExport.class);
}
/**
* Delete export
*
* @param id
* identifier for the export object to delete
* @throws IsilonException
*/
public void deleteExport(String id) throws IsilonException {
delete(_baseUrl.resolve(URI_NFS_EXPORTS), id, "export");
}
/**
* Delete export in access zone
*
* @param id
* identifier for the export object to delete
* @throws IsilonException
*/
public void deleteExport(String id, String zoneName) throws IsilonException {
String uriWithZoneName = getURIWithZoneName(id, zoneName);
delete(_baseUrl.resolve(URI_NFS_EXPORTS), uriWithZoneName, "export");
}
/* SmartQuotas */
/**
* List all smartquotas for given
*
* @param resumeToken
* @param pathBaseDir
* @return
* @throws IsilonException
*/
public IsilonList<IsilonSmartQuota> listFileQuotas(String resumeToken) throws IsilonException {
URI uri = URI_QUOTAS;
StringBuffer URLBuffer = new StringBuffer(_baseUrl.resolve(uri).toString());
URLBuffer.append("?path=").append("&recurse_path_children=true&type=directory");
uri = URI.create(URLBuffer.toString());
sLogger.info("get list of smart quotas of type directory for uri {}", uri.toString());
uri = _baseUrl.resolve(uri);
return list(uri, "quotas", IsilonSmartQuota.class, resumeToken);
}
/**
* List all smartquotas
*
* @return ArrayList of IsilonSmartQuota objects
* @throws IsilonException
*/
public IsilonList<IsilonSmartQuota> listQuotas(String resumeToken) throws IsilonException {
return list(_baseUrl.resolve(URI_QUOTAS), "quotas", IsilonSmartQuota.class, resumeToken);
}
/**
* List all smartquotas for given accesszone
*
* @param resumeToken
* @param pathBaseDir
* @return
* @throws IsilonException
*/
public IsilonList<IsilonSmartQuota> listQuotas(String resumeToken, String pathBaseDir) throws IsilonException {
URI uri = URI_QUOTAS;
if (pathBaseDir != null) {
StringBuffer URLBuffer = new StringBuffer(_baseUrl.resolve(uri).toString());
URLBuffer.append("?path=").append(pathBaseDir).append("&recurse_path_children=true");
uri = URI.create(URLBuffer.toString());
sLogger.info("get list of smart quotas for pathbaseDir {} and uri {}", pathBaseDir, uri.toString());
} else {
uri = _baseUrl.resolve(uri);
}
return list(uri, "quotas", IsilonSmartQuota.class, resumeToken);
}
/**
* Create a smartquota
*
* @param path
* directory to set quota for
* @param thresholds
* optional long values for the thresholds if none
* specified, an un-enforced quota will be created otherwise, the
* first value is used for hard limit and rest ignored for now
* @return Identifier for the quota created
* @throws IsilonException
*/
public String createQuota(String path, Long... thresholds) throws IsilonException {
IsilonSmartQuota quota;
if (thresholds != null && thresholds.length > 0) {
quota = constructIsilonSmartQuotaObjectWithThreshold(path, "directory", null, false, false, thresholds);
quota.setContainer(true); // set to true, so user see hard limit not
// cluster size.
} else {
quota = new IsilonSmartQuota(path);
}
sLogger.debug("IsilonApi createQuota {} - start", path);
String quotaId = create(_baseUrl.resolve(URI_QUOTAS), "quota", quota);
sLogger.debug("IsilonApi createQuota {} - complete", path);
return quotaId;
}
/**
* Create a smartquota
*
* @param path
* directory to set quota for
* @param thresholds
* optional long values for the thresholds if none
* specified, an un-enforced quota will be created otherwise, the
* first value is used for hard limit and rest ignored for now
* @param bThresholdsIncludeOverhead
* value to indicate if overhead is
* to be included in the quota
* @param bIncludeSnapshots
* value to indicate if snapshot size is to be included
* in the quota
* @return Identifier for the quota created
* @throws IsilonException
*/
public String createQuota(String path, Long fsSize, boolean bThresholdsIncludeOverhead,
boolean bIncludeSnapshots, Long... thresholds) throws IsilonException {
IsilonSmartQuota quota;
// Isilon does not allow to create zero quota directory.
if (thresholds != null && thresholds.length > 0 && (thresholds[0] > 0 || thresholds[1] > 0 || thresholds[2] > 0)) {
quota = constructIsilonSmartQuotaObjectWithThreshold(path, "directory", fsSize, bThresholdsIncludeOverhead, bIncludeSnapshots,
thresholds);
if (thresholds[0] > 0) {
quota.setContainer(true); // set to true, so user see hard limit not cluster size.
}
} else {
quota = new IsilonSmartQuota(path, bThresholdsIncludeOverhead, bIncludeSnapshots);
}
sLogger.debug("IsilonApi createQuota {} - start", path);
String quotaId = create(_baseUrl.resolve(URI_QUOTAS), "quota", quota);
sLogger.debug("IsilonApi createQuota {} - complete", path);
return quotaId;
}
// If we want to provide the UI to enter quota we can re-use this
public IsilonSmartQuota constructIsilonSmartQuotaObjectWithThreshold(String path, String type, Long fsSize,
Boolean bThresholdsIncludeOverhead, Boolean bIncludeSnapshots, Long... thresholds) {
IsilonSmartQuota quota;
Long size = thresholds[0];
if (thresholds[0] == 0) {
size = fsSize;
}
switch (thresholds.length) {
case 2:
quota = new IsilonSmartQuota(path, type, thresholds[0],
(thresholds[1] * size) / 100, 0L, 0L, bThresholdsIncludeOverhead,
bIncludeSnapshots);
break;
case 3:
quota = new IsilonSmartQuota(path, type, thresholds[0],
(thresholds[1] * size) / 100,
(thresholds[2] * size) / 100, 0L, bThresholdsIncludeOverhead,
bIncludeSnapshots);
break;
case 4:
quota = new IsilonSmartQuota(path, type, thresholds[0],
(thresholds[1] * size) / 100,
(thresholds[2] * size) / 100,
(thresholds[3] * 60 * 60 * 24), bThresholdsIncludeOverhead,
bIncludeSnapshots);
break;
default:
quota = new IsilonSmartQuota(path, type, thresholds[0], 0L, 0L, 0L, bThresholdsIncludeOverhead,
bIncludeSnapshots);
break;
}
return quota;
}
/**
* Modify a smartquota
*
* @param id
* Identifier for the quota to be modified
* @param q
* IsilonSmartQuota object with the modified values set
* @throws IsilonException
*/
public void modifyQuota(String id, IsilonSmartQuota q) throws IsilonException {
modify(_baseUrl.resolve(URI_QUOTAS), id, "quota", q);
}
/**
* Get smart quota
*
* @param id
* Identifier id the smartquota to get
* @return IsilonSmartQuota object
* @throws IsilonException
*/
public IsilonSmartQuota getQuota(String id) throws IsilonException {
return get(_baseUrl.resolve(URI_QUOTAS), id, "quotas", IsilonSmartQuota.class);
}
/**
* Delete a smart quota
*
* @param id
* Identifier of the smart quota object to delete
* @throws IsilonException
*/
public void deleteQuota(String id) throws IsilonException {
delete(_baseUrl.resolve(URI_QUOTAS), id, "quota");
}
/* Snapshots */
/**
* List all snapshots
*
* @return IsilonList of IsilonSnapshot objects
* @throws IsilonException
*/
public IsilonList<IsilonSnapshot> listSnapshots(String resumeToken) throws IsilonException {
return list(_baseUrl.resolve(URI_SNAPSHOTS), "snapshots", IsilonSnapshot.class, resumeToken);
}
/**
* List all snapshot for given access zone
*
* @param resumeToken
* @param pathBaseDir
* @return
* @throws IsilonException
*/
public IsilonList<IsilonSnapshot> listSnapshots(String resumeToken, String pathBaseDir) throws IsilonException {
URI uri = URI_SNAPSHOTS;
if (pathBaseDir != null) {
StringBuffer URLBuffer = new StringBuffer(_baseUrl.resolve(uri).toString());
URLBuffer.append("?path=").append(pathBaseDir).append("&recurse_path_children=true");
uri = URI.create(URLBuffer.toString());
sLogger.info("get list of snapshots for pathbaseDir {} and uri {} .", pathBaseDir, uri.toString());
} else {
uri = _baseUrl.resolve(uri);
}
return list(uri, "snapshots", IsilonSnapshot.class, resumeToken);
}
/**
* List all snapshot created by policy
*
* @param resumeToken
* @param pathBaseDir
* @return
* @throws IsilonException
*/
public IsilonList<IsilonSnapshot> listSnapshotsCreatedByPolicy(String resumeToken, String policy) throws IsilonException {
if (policy == null || policy.isEmpty()) {
return null;
}
URI uri = URI_SNAPSHOTS;
StringBuffer URLBuffer = new StringBuffer(_baseUrl.resolve(uri).toString());
URLBuffer.append("?schedule=").append(policy);
uri = URI.create(URLBuffer.toString());
sLogger.info("get list of snapshots for policy {} and uri {} .", policy, uri.toString());
return list(uri, "snapshots", IsilonSnapshot.class, resumeToken);
}
/**
* Create snapshot
*
* @param name
* String label to be used for the snapshot
* @param path
* directory path to snapshot
* @return String identifier for the snapshot created
* @throws IsilonException
*/
public String createSnapshot(String name, String path) throws IsilonException {
return create(_baseUrl.resolve(URI_SNAPSHOTS), "snapshot", new IsilonSnapshot(name, path,
null, null));
}
/**
* Modify snapshot
*
* @param id
* Identifier for the snapshot to be modified
* @param s
* IsilonSnapshot object with the modified values
* @throws IsilonException
*/
public void modifySnapshot(String id, IsilonSnapshot s) throws IsilonException {
modify(_baseUrl.resolve(URI_SNAPSHOTS), id, "snapshot", s);
}
/**
* Get snapshot
*
* @param id
* Identifier of the snapshot to get
* @return IsilonSnapshot object
* @throws IsilonException
*/
public IsilonSnapshot getSnapshot(String id) throws IsilonException {
return get(_baseUrl.resolve(URI_SNAPSHOTS), id, "snapshots", IsilonSnapshot.class);
}
/**
* Delete a snapshot
*
* @param id
* Identifier of the snapshot to delete
* @throws IsilonException
*/
public void deleteSnapshot(String id) throws IsilonException {
delete(_baseUrl.resolve(URI_SNAPSHOTS), id, "snapshot");
}
/* SMB Shares */
/**
* List all SMB Shares
*
* @return IsilonList of IsilonSMBShare objects
* @throws IsilonException
*/
public IsilonList<IsilonSMBShare> listShares(String resumeToken) throws IsilonException {
return list(_baseUrl.resolve(URI_SMB_SHARES), "shares", IsilonSMBShare.class, resumeToken);
}
/**
* List all SMB Shares
*
* @return IsilonList of IsilonSMBShare objects
* @throws IsilonException
*/
public IsilonList<IsilonSMBShare> listShares(String resumeToken, String zoneName) throws IsilonException {
URI uri = URI_SMB_SHARES;
if (zoneName != null) {
String baseUrl = getURIWithZoneName(_baseUrl.resolve(uri).toString(), zoneName);
uri = URI.create(baseUrl);
sLogger.info("get list of shares for accesszone {} and uri {}", zoneName, uri.toString());
} else {
uri = _baseUrl.resolve(uri);
}
return list(_baseUrl.resolve(uri), "shares", IsilonSMBShare.class, resumeToken);
}
/**
* Create SMB share
*
* @param name
* @param path
* Path to create the share
* @param desc
* Description
* @param host
* Host for access
* @return Identifier of the SMB share created
* @throws IsilonException
*/
public String createShare(String name, String path, String desc, String host)
throws IsilonException {
return create(_baseUrl.resolve(URI_SMB_SHARES), "share", new IsilonSMBShare(name, path,
desc, host));
}
/**
* Create Isilon SMB share.
*
* @param smbFileShare
* @return Identifier of the SMB share created
* @throws IsilonException
*/
public String createShare(IsilonSMBShare smbFileShare) throws IsilonException {
return create(_baseUrl.resolve(URI_SMB_SHARES), "share", smbFileShare);
}
/**
* Create Isilon SMB share on access zone.
*
* @param smbFileShare
* @return Identifier of the SMB share created
* @throws IsilonException
*/
public String createShare(IsilonSMBShare smbFileShare, String zoneName) throws IsilonException {
String baseUrl = getURIWithZoneName(_baseUrl.resolve(URI_SMB_SHARES).toString(), zoneName);
URI uri = URI.create(baseUrl);
return create(uri, "share", smbFileShare);
}
/**
* Modify SMB share
*
* @param id
* Identifier for the SMB share to modify
* @param s
* IsilonSMBShare object with the modified values set
* @throws IsilonException
*/
public void modifyShare(String id, IsilonSMBShare s) throws IsilonException {
modify(_baseUrl.resolve(URI_SMB_SHARES), id, "share", s);
}
/**
* Modify SMB share in access zone
*
* @param id
* Identifier for the SMB share to modify
* @param s
* IsilonSMBShare object with the modified values set
* @throws IsilonException
*/
public void modifyShare(String id, String zoneName, IsilonSMBShare s) throws IsilonException {
String uriWithZoneName = getURIWithZoneName(id, zoneName);
modify(_baseUrl.resolve(URI_SMB_SHARES), uriWithZoneName, "share", s);
}
/**
* Get SMB share properties
*
* @param id
* Identifier of the SMB share to get
* @return IsilonSMBShare object
* @throws IsilonException
*/
public IsilonSMBShare getShare(String id) throws IsilonException {
return get(_baseUrl.resolve(URI_SMB_SHARES), id, "shares", IsilonSMBShare.class);
}
/**
* Get SMB share properties on access zone
*
* @param id
* Identifier of the SMB share to get
* @return IsilonSMBShare object
* @throws IsilonException
*/
public IsilonSMBShare getShare(String id, String zoneName) throws IsilonException {
String uriWithZoneName = getURIWithZoneName(id, zoneName);
return get(_baseUrl.resolve(URI_SMB_SHARES), uriWithZoneName, "shares", IsilonSMBShare.class);
}
/**
* Delete SMB share
*
* @param id
* Identifier of the SMB share to delete
* @throws IsilonException
*/
public void deleteShare(String id) throws IsilonException {
delete(_baseUrl.resolve(URI_SMB_SHARES), id, "share");
}
/**
* Delete SMB share in access zone
*
* @param id
* Identifier of the SMB share to delete
* @throws IsilonException
*/
public void deleteShare(String id, String zoneName) throws IsilonException {
String uriWithZoneName = getURIWithZoneName(id, zoneName);
delete(_baseUrl.resolve(URI_SMB_SHARES), uriWithZoneName, "share");
}
/**
* Modify NFS ACL
*
* @param path
* path for the directory or file system to set ACL
* @param IsilonNFSACL
* object with the modified values set
* @throws IsilonException
*/
public void modifyNFSACL(String fspath, IsilonNFSACL acl) throws IsilonException {
try {
fspath = fspath.substring(1);// remove '/' prefix
fspath = URLEncoder.encode(fspath, "UTF-8");
fspath = fspath.concat("?acl");// add suffix ?acl
} catch (UnsupportedEncodingException e) {
sLogger.error("UnsupportedEncodingException occured", e);
}
put(_baseUrl.resolve(URI_IFS), fspath, "ACL", acl);
}
/**
* Get NFS ACL properties
*
* @param path
* Identifier of the SMB share to get
* @return IsilonNFSACL object
* @throws IsilonException
*/
public IsilonNFSACL getNFSACL(String fspath) throws IsilonException {
try {
fspath = fspath.substring(1);// remove '/' prefix
fspath = URLEncoder.encode(fspath, "UTF-8");
fspath = fspath.concat("?acl");// add suffix ?acl
} catch (UnsupportedEncodingException e) {
sLogger.error("UnsupportedEncodingException occured", e);
}
return getObj(_baseUrl.resolve(URI_IFS), fspath, IsilonNFSACL.class);
}
/**
* Get storage pools.
*
* @return storage pools
* @throws IsilonException
*/
public List<? extends IsilonPool> getStoragePools() throws IsilonException {
IsilonList<IsilonStoragePool> pools = list(_baseUrl.resolve(URI_STORAGE_POOLS),
"storagepools", IsilonStoragePool.class, null);
return pools.getList();
}
/**
* Get disk pools for OneFS version < 7.2
*
* @return disk pools
* @throws IsilonException
*/
public List<? extends IsilonPool> getDiskPools() throws IsilonException {
IsilonList<IsilonDiskPool> pools = list(_baseUrl.resolve(URI_DISK_POOLS),
"diskpools", IsilonDiskPool.class, null);
return pools.getList();
}
/**
* Get storage ports.
*
* @return storage ports
* @throws IsilonException
*/
public List<IsilonStoragePort> getSmartConnectPorts() throws IsilonException {
IsilonList<IsilonStoragePort> ports = list(_baseUrl.resolve(URI_STORAGE_PORTS), "zones",
IsilonStoragePort.class, null);
return ports.getList();
}
public IsilonSmartConnectInfo getSmartConnectInfo() throws IsilonException {
ClientResponse clientResp = null;
try {
clientResp = _client.get(_baseUrl.resolve(URI_STORAGE_PORTS));
if (clientResp.getStatus() != 200) {
throw IsilonException.exceptions.getStorageConnectionInfoFailedOnIsilonArray(clientResp.getStatus());
}
JSONObject resp = clientResp.getEntity(JSONObject.class);
sLogger.debug(resp.toString());
IsilonSmartConnectInfo info = new Gson().fromJson(SecurityUtils.sanitizeJsonString(resp.toString()),
IsilonSmartConnectInfo.class);
return info;
} catch (Exception e) {
String response = String.format("%1$s", (clientResp == null) ? "" : clientResp);
throw IsilonException.exceptions.getStorageConnectionInfoFailedOnIsilonArrayExc(response, e);
} finally {
if (clientResp != null) {
clientResp.close();
}
}
}
public IsilonSmartConnectInfoV2 getSmartConnectInfoV2() throws IsilonException {
ClientResponse clientResp = null;
try {
clientResp = _client.get(_baseUrl.resolve(URI_STORAGE_PORTS));
if (clientResp.getStatus() != 200) {
sLogger.debug("Response: Exception :" + clientResp.toString());
throw new IsilonException(clientResp.getStatus() + "");
}
IsilonSmartConnectInfoV2 info = null;
String responseString = null;
try {
responseString = clientResp.getEntity(String.class);
sLogger.debug("Response:" + responseString);
info = new Gson().fromJson(SecurityUtils.sanitizeJsonString(responseString),
IsilonSmartConnectInfoV2.class);
} catch (Exception e) {
sLogger.debug("Got Exception trying to get Json out of it " + e);
sLogger.debug("Response:String:" + responseString);
String fixedString = "{\"settings\":" + responseString + "}";
sLogger.debug("Fixed:String:" + fixedString);
info = new Gson().fromJson(SecurityUtils.sanitizeJsonString(fixedString),
IsilonSmartConnectInfoV2.class);
}
return info;
} catch (Exception e) {
String response = String.format("%1$s", (clientResp == null) ? "" : clientResp);
throw new IsilonException(response, e);
} finally {
if (clientResp != null) {
clientResp.close();
}
}
}
/**
* get the list of access zone
*
* @return
* @throws IsilonException
*/
public List<IsilonAccessZone> getAccessZones(String resumeToken) throws IsilonException {
IsilonList<IsilonAccessZone> accessZoneIsilonList = list(_baseUrl.resolve(URI_ACCESS_ZONES),
"zones", IsilonAccessZone.class, resumeToken);
return accessZoneIsilonList.getList();
}
/**
* get the list of network pools
*
* @return
* @throws IsilonException
*/
public List<IsilonNetworkPool> getNetworkPools(String resumeToken) throws IsilonException {
IsilonList<IsilonNetworkPool> accessZoneIsilonList = list(_baseUrl.resolve(URI_NETWORK_POOLS),
"pools", IsilonNetworkPool.class, resumeToken);
return accessZoneIsilonList.getList();
}
/**
* Get list of events from the url
*
* @param url
* @param firmwareVersion
* : Isilon version
* @return ArrayList of IsilonEvent objects
* @throws IsilonException
*/
private IsilonList<IsilonEvent> getEvents(URI url, String firmwareVersion) throws IsilonException {
// Get list of ISILON events using eventlists if ISILON version is OneFS8.0 or more else using events.
if (firmwareVersion != null && firmwareVersion.startsWith("8")) {
List<IsilonOneFS8Event> eventLists = list(url, "eventlists", IsilonOneFS8Event.class, null).getList();
IsilonList<IsilonEvent> isilonEventList = new IsilonList<IsilonEvent>();
for (IsilonOneFS8Event eventFS8 : eventLists) {
for (Events event : eventFS8.getEvents()) {
IsilonEvent isilonEvent = new IsilonEvent();
isilonEvent.devid = event.devid;
isilonEvent.event_type = event.event;
isilonEvent.id = event.id;
isilonEvent.message = event.message;
isilonEvent.severity = event.severity;
isilonEvent.start = event.time;
isilonEvent.specifiers = event.getSpecifier();
isilonEvent.value = event.value;
isilonEventList.add(isilonEvent);
}
}
return isilonEventList;
}
return list(url, "events", IsilonEvent.class, null);
}
/**
* Get the list of events
*
* @return IsilonList of IsilonEvent objects
* @throws IsilonException
*/
public IsilonList<IsilonEvent> listEvents(String resumeToken) throws IsilonException {
return list(_baseUrl.resolve(URI_EVENTS), "events", IsilonEvent.class, resumeToken);
}
/**
* Get the list of events in the time range
*
* @param begin
* number of seconds relative to current (e.g. -3600 for 1hr
* back)
* @param end
* number of seconds relative to current
* @param firmwareVersion
* : Isilon version
* @return ArrayList of IsilonEvent objects
* @throws IsilonException
*/
public IsilonList<IsilonEvent> queryEvents(long begin, long end, String firmwareVersion) throws IsilonException {
// In Isilon API, 0 value for time in query is used as beginning of
// absolute time.
// We use 0 value for time to indicate current time on remote host.
// To use current end time on remote host in Isilon API query, do not
// specify end time in the query.
// TODO: Need to find out what does "intersect" query parameter mean.
// Default behavior is intersect = true.
String query = (end != 0) ? String.format("?begin=%1$d&end=%2$d", begin, end) : String
.format("?begin=%1$d", begin);
// If ISILON version is OneFS8.0 then get events URI will be /platform/3/event/eventlists/.
if (firmwareVersion != null && firmwareVersion.startsWith("8")) {
return getEvents(_baseUrl.resolve(URI_ONEFS8_EVENTS.resolve(query)), firmwareVersion);
}
return getEvents(_baseUrl.resolve(URI_EVENTS.resolve(query)), firmwareVersion);
}
/**
* Remove leading slash (if it exists) to use as relative path (with a base
* URI)
*
* @param fsPath
* @return
*/
private String scrubPath(String fsPath) {
if (fsPath.charAt(0) == '/') {
return fsPath.substring(1);
}
return fsPath;
}
/**
* Get current statistics
*
* @param key
* Stats's key
* @return map of node number to IsilonStats.StatValueCurrent
* @throws IsilonException
*/
public <T> HashMap<String, IsilonStats.StatValueCurrent<T>> getStatsCurrent(String key,
Type valueType) throws IsilonException {
ClientResponse clientResp = null;
try {
clientResp = _client.get(_baseUrl.resolve(URI_STATS.resolve(String.format(
"current?key=%1$s&devid=0", key))));
if (clientResp.getStatus() != 200) {
throw IsilonException.exceptions.getCurrentStatisticsFailedOnIsilonArray(clientResp.getStatus());
}
JSONObject resp = clientResp.getEntity(JSONObject.class);
JSONObject obj = resp.getJSONObject(key);
HashMap<String, IsilonStats.StatValueCurrent<T>> retMap = new HashMap<String, IsilonStats.StatValueCurrent<T>>();
Iterator it = obj.keys();
while (it.hasNext()) {
String entryKey = it.next().toString();
JSONObject entryObj = obj.getJSONObject(entryKey);
IsilonStats.StatValueCurrent<T> statValueCurrent = new IsilonStats.StatValueCurrent<T>();
statValueCurrent.time = entryObj.getLong("time");
statValueCurrent.error = new ArrayList<String>();
JSONArray jsonArray = entryObj.getJSONArray("error");
int len = jsonArray.length();
for (int i = 0; i < len; i++) {
String val = jsonArray.get(i).toString();
if (val.equals("0")) {
continue;
}
statValueCurrent.error.add(jsonArray.get(i).toString());
}
if (!entryObj.has("value")) {
throw IsilonException.exceptions.getCurrentStatisticsFailedOnIsilonArrayErr(key,
statValueCurrent.error.toString());
}
statValueCurrent.value = new Gson()
.fromJson(SecurityUtils.sanitizeJsonString(entryObj.getString("value")), valueType);
retMap.put(entryKey, statValueCurrent);
}
return retMap;
} catch (Exception e) {
String response = String.format("%1$s", (clientResp == null) ? "" : clientResp);
throw IsilonException.exceptions.getCurrentStatisticsFailedOnIsilonArrayExc(response, e);
} finally {
if (clientResp != null) {
clientResp.close();
}
}
}
/**
* Get statistic history
*
* @param key
* Stats's key
* @param valueType
* @return IsilonStats.StatValueHistory
* @throws IsilonException
*/
public <T> HashMap<String, IsilonStats.StatValueHistory<T>> getStatsHistory(String key,
long begin, Type valueType) throws IsilonException {
ClientResponse clientResp = null;
try {
clientResp = _client.get(_baseUrl.resolve(URI_STATS.resolve(String.format(
"history?key=%1$s&devid=0&begin=%2$s", key, begin))));
if (clientResp.getStatus() != 200) {
throw IsilonException.exceptions.getStatisticsHistoryFailedOnIsilonArray(clientResp.getStatus());
}
JSONObject resp = clientResp.getEntity(JSONObject.class);
JSONObject obj = resp.getJSONObject(key);
HashMap<String, IsilonStats.StatValueHistory<T>> retMap = new HashMap<String, IsilonStats.StatValueHistory<T>>();
Iterator it = obj.keys();
while (it.hasNext()) {
String entryKey = it.next().toString();
JSONObject entryObj = obj.getJSONObject(entryKey);
IsilonStats.StatValueHistory<T> statValueHistory = new IsilonStats.StatValueHistory<T>();
if (entryObj.has("error")) {
JSONArray array = entryObj.getJSONArray("error");
for (int i = 0; i < array.length(); i++) {
String val = array.get(i).toString();
if (val.equals("0")) {
continue;
}
statValueHistory.error.add(array.get(i).toString());
}
}
if (entryObj.has("values")) {
JSONArray array = entryObj.getJSONArray("values");
for (int i = 0; i < array.length(); i++) {
// each value is again an array, [timestamp, T]
JSONArray entry = (JSONArray) array.get(i);
if (entry.length() == 2) {
long timestamp = entry.getLong(0);
T value = new Gson().fromJson(SecurityUtils.sanitizeJsonString(entry.getString(1)), valueType);
statValueHistory.values.put(timestamp, value);
}
}
}
if (statValueHistory.error.isEmpty() && statValueHistory.values.isEmpty()) {
continue;
}
retMap.put(entryKey, statValueHistory);
}
return retMap;
} catch (Exception e) {
String response = String.format("%1$s", (clientResp == null) ? "" : clientResp);
throw IsilonException.exceptions.getStatisticsHistoryFailedOnIsilonArrayExc(response, e);
} finally {
if (clientResp != null) {
clientResp.close();
}
}
}
/**
* Get statistic protocols
*
* @return protocol list
* @throws Exception
* IsilonException
*/
public ArrayList<IsilonStats.Protocol> getStatsProtocols() throws IsilonException {
ArrayList<IsilonStats.Protocol> statProtocols = new ArrayList<IsilonStats.Protocol>();
ClientResponse clientResp = null;
try {
clientResp = _client.get(_baseUrl.resolve(URI_STATS.resolve("protocols")));
if (clientResp.getStatus() != 200) {
throw IsilonException.exceptions.getStatisticsProtocolFailedOnIsilonArray(clientResp.getStatus());
}
// ObjectMapper mapper = new ObjectMapper();
JSONObject resp = clientResp.getEntity(JSONObject.class);
// IsilonStats.Protocols protocols =
// mapper.readValue(resp.toString(), IsilonStats.Protocols.class);
JSONArray protocols = resp.getJSONArray("protocols");
for (int i = 0; i < protocols.length(); i++) {
JSONObject protocol = protocols.getJSONObject(i);
statProtocols.add(new Gson().fromJson(SecurityUtils.sanitizeJsonString(protocol.toString()),
IsilonStats.Protocol.class));
}
return statProtocols;
} catch (Exception e) {
String response = String.format("%1$s", (clientResp == null) ? "" : clientResp);
throw IsilonException.exceptions.getStatisticsProtocolFailedOnIsilonArrayExc(response, e);
} finally {
if (clientResp != null) {
clientResp.close();
}
}
}
/**
* Process http error response from Isilon
*
* @param operationKey
* opertaion key: list, create, delete, modify, etc
* @param objectKey
* object type: export, snapshot, smb share,...
* @param httpStatus
* http status
* @param errorEntity
* entity of error response
* @throws IsilonException
* @throws JSONException
*/
private void processErrorResponse(String operationKey, String objectKey, int httpStatus,
JSONObject errorEntity) throws IsilonException, JSONException {
if (errorEntity == null) {
throw IsilonException.exceptions.processErrorResponseFromIsilon(operationKey,
objectKey, httpStatus, _baseUrl);
} else if (errorEntity.has("errors")) {
throw IsilonException.exceptions.processErrorResponseFromIsilonMsg(operationKey,
objectKey, httpStatus, _baseUrl, errorEntity.getString("errors"));
} else if (errorEntity.has("message")) {
throw IsilonException.exceptions.processErrorResponseFromIsilonMsg(operationKey,
objectKey, httpStatus, _baseUrl, errorEntity.getString("message"));
} else {
throw IsilonException.exceptions.processErrorResponseFromIsilon(operationKey,
objectKey, httpStatus, _baseUrl);
}
}
/**
* Checks to see if the NFSv4 service is enabled on the isilon device
*
* @return boolean true if exists, false otherwise
*/
public boolean nfsv4Enabled(String firmwareVersion) throws IsilonException {
ClientResponse resp = null;
boolean isNfsv4Enabled = false;
try {
sLogger.debug("IsilonApi check nfsV4 support retrieve global status - start");
// Check if ISILON ONEFS version is 8.0 and more to get NFSV4 details
if (firmwareVersion.startsWith("8")) {
resp = _client.get(_baseUrl.resolve(URI_ARRAY_GLOBAL_STATUS_ONEFS8));
} else {
resp = _client.get(_baseUrl.resolve(URI_ARRAY_GLOBAL_STATUS));
}
sLogger.debug("IsilonApi check nfsV4 support retrieve global status - complete");
JSONObject jsonResp = resp.getEntity(JSONObject.class);
isNfsv4Enabled = Boolean.parseBoolean(jsonResp.getJSONObject(
"settings").getString("nfsv4_enabled"));
sLogger.info("IsilonApi nfsv4 enable/disable is set to {}",
isNfsv4Enabled);
} catch (Exception e) {
throw IsilonException.exceptions.unableToConnect(_baseUrl, e);
} finally {
if (resp != null) {
resp.close();
}
}
return isNfsv4Enabled;
}
/**
* Checks to see if the SyncIQ service is enabled on the isilon device
*
* @return boolean true if exists, false otherwise
*/
public boolean isSyncIQEnabled(String firmwareVersion) throws IsilonException {
ClientResponse resp = null;
boolean isSyncIqEnabled = false;
try {
// Verify the Sync service is enable or not
// JSON response for the below should have service=on
resp = _client.get(_baseUrl.resolve(URI_SYNCIQ_SERVICE_STATUS));
JSONObject jsonResp = resp.getEntity(JSONObject.class);
if (jsonResp.has("settings") && jsonResp.getJSONObject("settings") != null) {
if (jsonResp.getJSONObject("settings").has("service")) {
String syncService = jsonResp.getJSONObject("settings").getString("service");
if (syncService != null && !syncService.isEmpty()) {
sLogger.info("IsilonApi - SyncIQ service status {} ", syncService);
if ("on".equalsIgnoreCase(syncService)) {
isSyncIqEnabled = true;
}
}
}
}
} catch (Exception e) {
throw IsilonException.exceptions.unableToConnect(_baseUrl, e);
} finally {
if (resp != null) {
resp.close();
}
}
return isSyncIqEnabled;
}
/**
* Get SyncIq license information from the Isilon array
*
* @return IsilonReplicationLicenseInfo object
* @throws IsilonException
* @throws JSONException
*/
public String getReplicationLicenseInfo() throws IsilonException, JSONException {
String licenseStatus = "Unknown";
ClientResponse clientResp = _client.get(_baseUrl.resolve(URI_REPLICATION_LICENSE_INFO));
JSONObject jsonResp = clientResp.getEntity(JSONObject.class);
if (jsonResp.has("status")) {
licenseStatus = jsonResp.get("status").toString();
return licenseStatus;
}
return licenseStatus;
}
/**
* Get Replication Policy information from the Isilon array
*
* @return IsilonSyncPolicy object
* @throws IsilonException
*/
public IsilonSyncPolicy getReplicationPolicy(String id) throws IsilonException {
return get(_baseUrl.resolve(URI_REPLICATION_POLICIES), id, "policies", IsilonSyncPolicy.class);
}
/**
* Get Replication Policy information from the Isilon array using oneFS v8 above
*
* @return IsilonSyncPolicy object
* @throws IsilonException
*/
public IsilonSyncPolicy8Above getReplicationPolicy8above(String id) throws IsilonException {
return get(_baseUrl.resolve(URI_REPLICATION_POLICIES_8), id, "policies", IsilonSyncPolicy8Above.class);
}
/**
* Get All Replication Policies information from the Isilon array
*
* @return IsilonList<IsilonSyncPolicy>
* @throws IsilonException
*/
public IsilonList<IsilonSyncPolicy> getReplicationPolicies() throws IsilonException {
return list(_baseUrl.resolve(URI_REPLICATION_POLICIES), "policies", IsilonSyncPolicy.class, "");
}
/**
* Get All Replication Policies information from the Isilon array
*
* @return IsilonList<IsilonSyncPolicy>
* @throws IsilonException
*/
public IsilonList<IsilonSyncPolicy8Above> getReplicationPolicies8above() throws IsilonException {
return list(_baseUrl.resolve(URI_REPLICATION_POLICIES_8), "policies", IsilonSyncPolicy8Above.class, "");
}
/**
* Get Target Replication Policy information from the Isilon array
*
* @return IsilonSyncPolicy object
* @throws IsilonException
*/
public IsilonSyncTargetPolicy getTargetReplicationPolicy(String id) throws IsilonException {
return get(_baseUrl.resolve(URI_TARGET_REPLICATION_POLICIES), id, "policies", IsilonSyncTargetPolicy.class);
}
/**
* Create Replication Policy
*
* @param replicationPolicy
* IsilonSyncPolicy object
* @return String identifier for the policy created
* @throws IsilonException
*/
public String createReplicationPolicy(IsilonSyncPolicy replicationPolicy) throws IsilonException {
return create(_baseUrl.resolve(URI_REPLICATION_POLICIES), "policies", replicationPolicy);
}
/**
* Create Replication Policy for isilon array using oneFSv8 and above
*
* @param replicationPolicy
* IsilonSyncPolicy object
* @return String identifier for the policy created
* @throws IsilonException
*/
public String createReplicationPolicy8above(IsilonSyncPolicy8Above replicationPolicy) throws IsilonException {
return create(_baseUrl.resolve(URI_REPLICATION_POLICIES_8), "policies", replicationPolicy);
}
/**
* Modify Replication Policy
*
* @param id
* identifier/name of the Replication Policy to modify
* @param syncPolicy
* IsilonSyncPolicy object with the modified properties
* @throws IsilonException
*/
public void modifyReplicationPolicy(String id, IsilonSyncPolicy syncPolicy) throws IsilonException {
modify(_baseUrl.resolve(URI_REPLICATION_POLICIES), id, "policies", syncPolicy);
}
/**
* Modify Replication Policyfor isilon array using oneFSv8 and above
*
* @param id
* identifier/name of the Replication Policy to modify
* @param syncPolicy
* IsilonSyncPolicy object with the modified properties
* @throws IsilonException
*/
public void modifyReplicationPolicy8above(String id, IsilonSyncPolicy8Above syncPolicy) throws IsilonException {
modify(_baseUrl.resolve(URI_REPLICATION_POLICIES_8), id, "policies", syncPolicy);
}
/**
* Delete replication policy
*
* @param id
* identifier for the replication policy object to delete
* @throws IsilonException
*/
public void deleteReplicationPolicy(String id) throws IsilonException {
delete(_baseUrl.resolve(URI_REPLICATION_POLICIES), id, "policies");
}
/**
* Get Replication Jobs information from the Isilon array
*
* @param id
* identifier for the replication policy
* @return Replication Jobs object
* @throws IsilonException
*/
public IsilonSyncJob getReplicationJob(String id) throws IsilonException {
return get(_baseUrl.resolve(URI_REPLICATION_JOBS), id, "jobs", IsilonSyncJob.class);
}
/**
* Start a Replication Job
*
* @param IsilonSyncJob
* Object
* @return policy_name
* @throws IsilonException
*/
public String modifyReplicationJob(IsilonSyncJob job) throws IsilonException {
return create(_baseUrl.resolve(URI_REPLICATION_JOBS), "jobs", job);
}
/**
* Modify Replication Job
*
* @param id
* identifier/name of the Replication Policy to modify
* @param syncPolicy
* IsilonSyncPolicy object with the modified properties
* @throws IsilonException
*/
public void modifyReplicationJob(String id, IsilonSyncJob job) throws IsilonException {
modify(_baseUrl.resolve(URI_REPLICATION_JOB), id, "jobs", job);
}
/**
* Get Replication Reports information from the Isilon array
*
* @param Name
* for the replication policy
* @return Replication Report Object
* @throws IsilonException
*/
public IsilonList<IsilonSyncPolicyReport> getReplicationPolicyReports(String policyName) throws IsilonException {
URI uri = URI.create(URI_REPLICATION_POLICY_REPORTS.toString() + policyName);
return list(_baseUrl.resolve(uri), "reports", IsilonSyncPolicyReport.class, "");
}
/**
* Get Target Replication Reports information from the Isilon array
*
* @param Name
* for the replication policy
* @return Replication Report Object
* @throws IsilonException
*/
public IsilonList<IsilonSyncPolicyReport> getTargetReplicationPolicyReports(String policyName) throws IsilonException {
URI uri = URI.create(URI_TARGET_REPLICATION_POLICY_REPORTS.toString() + policyName);
return list(_baseUrl.resolve(uri), "reports", IsilonSyncPolicyReport.class, "");
}
private String getURIWithZoneName(String id, String zoneName) {
StringBuffer buffer = new StringBuffer(id);
buffer.append("?zone=");
String accesszoneName = zoneName.replace(" ", "%20");
buffer.append(accesszoneName);
return buffer.toString();
}
/**
* Checks the status of a license on Isilon
*
* @param licenseType
* type of the license for which the activation status is required
* @return licenseStatus Status of license
* @throws IsilonException
* @throws JSONException
*/
public String getLicenseInfo(IsilonLicenseType licenseType) throws IsilonException, JSONException {
ClientResponse resp = null;
String licenseStatus = "Unknown";
try {
// Verify whether specified license is activated on ISILON array or not
resp = _client.get(_baseUrl.resolve(licenseMap.get(licenseType)));
JSONObject jsonResp = resp.getEntity(JSONObject.class);
if (jsonResp.has("status")) {
licenseStatus = jsonResp.get("status").toString();
return licenseStatus;
}
} catch (Exception e) {
throw IsilonException.exceptions.unableToConnect(_baseUrl, e);
} finally {
if (resp != null) {
resp.close();
}
}
return licenseStatus;
}
/**
* Checks to see if the SnapshotIQ service is enabled on the isilon device
*
* @return licenseStatus Status of SnapshotIQ license
* @throws IsilonException
* @throws JSONException
*/
public String snapshotIQLicenseInfo() throws IsilonException, JSONException {
ClientResponse resp = null;
String licenseStatus = "Unknown";
try {
// Verify whether SnapshotIQ service is enabled on ISILON array or not
resp = _client.get(_baseUrl.resolve(URI_SNAPSHOTIQ_LICENSE_INFO));
JSONObject jsonResp = resp.getEntity(JSONObject.class);
if (jsonResp.has("status")) {
licenseStatus = jsonResp.get("status").toString();
}
} catch (Exception e) {
throw IsilonException.exceptions.unableToConnect(_baseUrl, e);
} finally {
if (resp != null) {
resp.close();
}
}
sLogger.info("Isilon snapshotIQ license status is {}", licenseStatus);
return licenseStatus;
}
}