org.apache.cloudstack.storage.datastore.util.DateraUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.cloudstack.storage.datastore.util.DateraUtil.java

Source

// 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 com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.utils.StringUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringTokenizer;
import java.util.UUID;

public class DateraUtil {

    private static final Logger s_logger = Logger.getLogger(DateraUtil.class);
    private static final String API_VERSION = "v2";

    public static final String PROVIDER_NAME = "Datera";

    private static final String HEADER_AUTH_TOKEN = "auth-token";
    private static final String HEADER_CONTENT_TYPE = "Content-type";
    private static final String HEADER_VALUE_JSON = "application/json";

    public static final String MANAGEMENT_VIP = "mVip";
    public static final String STORAGE_VIP = "sVip";

    public static final String MANAGEMENT_PORT = "mPort";
    public static final String STORAGE_PORT = "sPort";

    private static final int DEFAULT_MANAGEMENT_PORT = 7717;
    private static final int DEFAULT_STORAGE_PORT = 3260;
    private static final int DEFAULT_NUM_REPLICAS = 3;

    public static final String CLUSTER_ADMIN_USERNAME = "clusterAdminUsername";
    public static final String CLUSTER_ADMIN_PASSWORD = "clusterAdminPassword";

    public static final String CLUSTER_DEFAULT_MIN_IOPS = "clusterDefaultMinIops";
    public static final String CLUSTER_DEFAULT_MAX_IOPS = "clusterDefaultMaxIops";
    public static final String NUM_REPLICAS = "numReplicas";

    public static final String STORAGE_POOL_ID = "DateraStoragePoolId";
    public static final String VOLUME_SIZE = "DateraVolumeSize";
    public static final String VOLUME_ID = "DateraVolumeId";
    public static final String SNAPSHOT_ID = "DateraSnapshotId";
    public static final String TEMP_VOLUME_ID = "tempVolumeId";

    public static final int MAX_IOPS = 30000; // max IOPS that can be assigned to a volume

    public static final String INITIATOR_GROUP_PREFIX = "Cloudstack-InitiatorGroup";
    public static final String INITIATOR_PREFIX = "Cloudstack-Initiator";
    public static final String APPINSTANCE_PREFIX = "Cloudstack";

    public static final int MIN_NUM_REPLICAS = 1;
    public static final int MAX_NUM_REPLICAS = 5;

    public static final int POLL_TIMEOUT_MS = 3000;
    public static final String STATE_AVAILABLE = "available";
    public static final int DEFAULT_RETRIES = 3;

    private static Gson gson = new GsonBuilder().create();

    private int managementPort;
    private String managementIp;
    private String username;
    private String password;

    private static final String SCHEME_HTTP = "http";

    public DateraUtil(String managementIp, int managementPort, String username, String password) {
        this.managementPort = managementPort;
        this.managementIp = managementIp;
        this.username = username;
        this.password = password;
    }

    public static String login(DateraObject.DateraConnection conn)
            throws UnsupportedEncodingException, DateraObject.DateraError {

        DateraObject.DateraLogin loginParams = new DateraObject.DateraLogin(conn.getUsername(), conn.getPassword());
        HttpPut loginReq = new HttpPut(generateApiUrl("login"));

        StringEntity jsonParams = new StringEntity(gson.toJson(loginParams));
        loginReq.setEntity(jsonParams);

        String response = executeHttp(conn, loginReq);
        DateraObject.DateraLoginResponse loginResponse = gson.fromJson(response,
                DateraObject.DateraLoginResponse.class);

        return loginResponse.getKey();

    }

    public static Map<String, DateraObject.AppInstance> getAppInstances(DateraObject.DateraConnection conn)
            throws DateraObject.DateraError {

        HttpGet getAppInstancesReq = new HttpGet(generateApiUrl("app_instances"));
        String response = null;

        response = executeApiRequest(conn, getAppInstancesReq);

        Type responseType = new TypeToken<Map<String, DateraObject.AppInstance>>() {
        }.getType();

        return gson.fromJson(response, responseType);
    }

    public static DateraObject.AppInstance getAppInstance(DateraObject.DateraConnection conn, String name)
            throws DateraObject.DateraError {

        HttpGet url = new HttpGet(generateApiUrl("app_instances", name));

        String response = null;
        try {
            response = executeApiRequest(conn, url);
            return gson.fromJson(response, DateraObject.AppInstance.class);
        } catch (DateraObject.DateraError dateraError) {
            if (DateraObject.DateraErrorTypes.NotFoundError.equals(dateraError)) {
                return null;
            } else {
                throw dateraError;
            }
        }
    }

    public static DateraObject.PerformancePolicy getAppInstancePerformancePolicy(DateraObject.DateraConnection conn,
            String appInstanceName) throws DateraObject.DateraError {

        HttpGet url = new HttpGet(generateApiUrl("app_instances", appInstanceName, "storage_instances",
                DateraObject.DEFAULT_STORAGE_NAME, "volumes", DateraObject.DEFAULT_VOLUME_NAME,
                "performance_policy"));

        try {
            String response = executeApiRequest(conn, url);
            return gson.fromJson(response, DateraObject.PerformancePolicy.class);
        } catch (DateraObject.DateraError dateraError) {
            if (DateraObject.DateraErrorTypes.NotFoundError.equals(dateraError)) {
                return null;
            } else {
                throw dateraError;
            }
        }

    }

    public static DateraObject.PerformancePolicy createAppInstancePerformancePolicy(
            DateraObject.DateraConnection conn, String appInstanceName, int totalIops)
            throws UnsupportedEncodingException, DateraObject.DateraError {

        HttpPost url = new HttpPost(generateApiUrl("app_instances", appInstanceName, "storage_instances",
                DateraObject.DEFAULT_STORAGE_NAME, "volumes", DateraObject.DEFAULT_VOLUME_NAME,
                "performance_policy"));

        DateraObject.PerformancePolicy performancePolicy = new DateraObject.PerformancePolicy(totalIops);

        url.setEntity(new StringEntity(gson.toJson(performancePolicy)));

        String response = executeApiRequest(conn, url);

        return gson.fromJson(response, DateraObject.PerformancePolicy.class);
    }

    public static void updateAppInstanceIops(DateraObject.DateraConnection conn, String appInstance, int totalIops)
            throws UnsupportedEncodingException, DateraObject.DateraError {

        if (getAppInstancePerformancePolicy(conn, appInstance) == null) {
            createAppInstancePerformancePolicy(conn, appInstance, totalIops);
        } else {

            HttpPut url = new HttpPut(generateApiUrl("app_instances", appInstance, "storage_instances",
                    DateraObject.DEFAULT_STORAGE_NAME, "volumes", DateraObject.DEFAULT_VOLUME_NAME,
                    "performance_policy"));

            DateraObject.PerformancePolicy performancePolicy = new DateraObject.PerformancePolicy(totalIops);

            url.setEntity(new StringEntity(gson.toJson(performancePolicy)));
            executeApiRequest(conn, url);
        }
    }

    public static void updateAppInstanceSize(DateraObject.DateraConnection conn, String appInstanceName,
            int newSize) throws UnsupportedEncodingException, DateraObject.DateraError {
        try {

            updateAppInstanceAdminState(conn, appInstanceName, DateraObject.AppState.OFFLINE);
            HttpPut url = new HttpPut(generateApiUrl("app_instances", appInstanceName, "storage_instances",
                    DateraObject.DEFAULT_STORAGE_NAME, "volumes", DateraObject.DEFAULT_VOLUME_NAME));

            DateraObject.Volume volume = new DateraObject.Volume(newSize);
            url.setEntity(new StringEntity(gson.toJson(volume)));
            executeApiRequest(conn, url);

        } finally {
            // bring it back online if something bad happened
            try {
                updateAppInstanceAdminState(conn, appInstanceName, DateraObject.AppState.ONLINE);
            } catch (Exception e) {
                s_logger.warn("Error getting appInstance " + appInstanceName + " back online ", e);
            }
        }
    }

    private static DateraObject.AppInstance createAppInstance(DateraObject.DateraConnection conn, String name,
            StringEntity appInstanceEntity) throws DateraObject.DateraError {

        HttpPost createAppInstance = new HttpPost(generateApiUrl("app_instances"));
        HttpGet getAppInstance = new HttpGet(generateApiUrl("app_instances", name));
        createAppInstance.setEntity(appInstanceEntity);
        String response = null;

        executeApiRequest(conn, createAppInstance);

        //create is async, do a get to fetch the IQN
        executeApiRequest(conn, getAppInstance);

        return pollVolumeAvailable(conn, name);
    }

    public static DateraObject.AppInstance createAppInstance(DateraObject.DateraConnection conn, String name,
            int size, int totalIops, int replicaCount)
            throws UnsupportedEncodingException, DateraObject.DateraError {

        DateraObject.AppInstance appInstance = new DateraObject.AppInstance(name, size, totalIops, replicaCount);
        StringEntity appInstanceEntity = new StringEntity(gson.toJson(appInstance));

        return createAppInstance(conn, name, appInstanceEntity);
    }

    public static DateraObject.AppInstance cloneAppInstanceFromVolume(DateraObject.DateraConnection conn,
            String name, String srcCloneName) throws UnsupportedEncodingException, DateraObject.DateraError {

        DateraObject.AppInstance srcAppInstance = getAppInstance(conn, srcCloneName);

        if (srcAppInstance == null) {
            throw new DateraObject.DateraError("NotFoundError", 404, null,
                    "Unable to find the base app instance to clone from");
        }

        String srcClonePath = srcAppInstance.getVolumePath();

        DateraObject.AppInstance appInstanceObj = new DateraObject.AppInstance(name, srcClonePath);

        StringEntity appInstanceEntity = new StringEntity(gson.toJson(appInstanceObj));
        DateraObject.AppInstance appInstance = createAppInstance(conn, name, appInstanceEntity);

        //bring it online
        updateAppInstanceAdminState(conn, name, DateraObject.AppState.ONLINE);

        return getAppInstance(conn, name);
    }

    public static DateraObject.AppInstance pollVolumeAvailable(DateraObject.DateraConnection conn,
            String appInstanceName) throws DateraObject.DateraError {

        int retries = DateraUtil.DEFAULT_RETRIES;
        DateraObject.AppInstance appInstance = null;
        do {
            appInstance = getAppInstance(conn, appInstanceName);
            try {
                Thread.sleep(DateraUtil.POLL_TIMEOUT_MS);
            } catch (InterruptedException e) {
                return null;
            }
            retries--;
        } while ((appInstance != null
                && !Objects.equals(appInstance.getVolumeOpState(), DateraUtil.STATE_AVAILABLE)) && retries > 0);
        return appInstance;
    }

    public static DateraObject.AppInstance pollAppInstanceAvailable(DateraObject.DateraConnection conn,
            String appInstanceName) throws DateraObject.DateraError {

        int retries = DateraUtil.DEFAULT_RETRIES;
        DateraObject.AppInstance appInstance = null;
        do {
            appInstance = getAppInstance(conn, appInstanceName);
            try {
                Thread.sleep(DateraUtil.POLL_TIMEOUT_MS);
            } catch (InterruptedException e) {
                return null;
            }
            retries--;
        } while ((appInstance != null
                && !Objects.equals(appInstance.getAdminState(), DateraObject.AppState.ONLINE.toString()))
                && retries > 0);
        return appInstance;
    }

    public static DateraObject.Initiator createInitiator(DateraObject.DateraConnection conn, String name,
            String iqn) throws DateraObject.DateraError, UnsupportedEncodingException {

        HttpPost req = new HttpPost(generateApiUrl("initiators"));

        DateraObject.Initiator initiator = new DateraObject.Initiator(name, iqn);
        StringEntity httpEntity = new StringEntity(gson.toJson(initiator));
        req.setEntity(httpEntity);

        return gson.fromJson(executeApiRequest(conn, req), DateraObject.Initiator.class);
    }

    public static DateraObject.Initiator getInitiator(DateraObject.DateraConnection conn, String iqn)
            throws DateraObject.DateraError {

        try {
            HttpGet getReq = new HttpGet(generateApiUrl("initiators", iqn));
            String response = executeApiRequest(conn, getReq);
            return gson.fromJson(response, DateraObject.Initiator.class);
        } catch (DateraObject.DateraError dateraError) {
            if (DateraObject.DateraErrorTypes.NotFoundError.equals(dateraError)) {
                return null;
            } else {
                throw dateraError;
            }
        }
    }

    public static void deleteInitiator(DateraObject.DateraConnection conn, String iqn)
            throws DateraObject.DateraError {

        HttpDelete req = new HttpDelete(generateApiUrl("initiators", iqn));
        executeApiRequest(conn, req);
    }

    public static DateraObject.InitiatorGroup createInitiatorGroup(DateraObject.DateraConnection conn, String name)
            throws UnsupportedEncodingException, DateraObject.DateraError {

        HttpPost createReq = new HttpPost(generateApiUrl("initiator_groups"));

        DateraObject.InitiatorGroup group = new DateraObject.InitiatorGroup(name, Collections.<String>emptyList());

        StringEntity httpEntity = new StringEntity(gson.toJson(group));
        createReq.setEntity(httpEntity);

        String response = executeApiRequest(conn, createReq);
        return gson.fromJson(response, DateraObject.InitiatorGroup.class);
    }

    public static void deleteInitatorGroup(DateraObject.DateraConnection conn, String name)
            throws DateraObject.DateraError {
        HttpDelete delReq = new HttpDelete(generateApiUrl("initiator_groups", name));
        executeApiRequest(conn, delReq);
    }

    public static DateraObject.InitiatorGroup getInitiatorGroup(DateraObject.DateraConnection conn, String name)
            throws DateraObject.DateraError {
        try {
            HttpGet getReq = new HttpGet(generateApiUrl("initiator_groups", name));
            String response = executeApiRequest(conn, getReq);
            return gson.fromJson(response, DateraObject.InitiatorGroup.class);

        } catch (DateraObject.DateraError dateraError) {
            if (DateraObject.DateraErrorTypes.NotFoundError.equals(dateraError)) {
                return null;
            } else {
                throw dateraError;
            }
        }
    }

    public static void updateInitiatorGroup(DateraObject.DateraConnection conn, String initiatorPath,
            String groupName, DateraObject.DateraOperation op)
            throws DateraObject.DateraError, UnsupportedEncodingException {

        DateraObject.InitiatorGroup initiatorGroup = getInitiatorGroup(conn, groupName);

        if (initiatorGroup == null) {
            throw new CloudRuntimeException("Unable to find initiator group by name " + groupName);
        }

        HttpPut addReq = new HttpPut(generateApiUrl("initiator_groups", groupName, "members"));

        DateraObject.Initiator initiator = new DateraObject.Initiator(initiatorPath, op);

        addReq.setEntity(new StringEntity(gson.toJson(initiator)));
        executeApiRequest(conn, addReq);
    }

    public static void addInitiatorToGroup(DateraObject.DateraConnection conn, String initiatorPath,
            String groupName) throws UnsupportedEncodingException, DateraObject.DateraError {
        updateInitiatorGroup(conn, initiatorPath, groupName, DateraObject.DateraOperation.ADD);
    }

    public static void removeInitiatorFromGroup(DateraObject.DateraConnection conn, String initiatorPath,
            String groupName) throws DateraObject.DateraError, UnsupportedEncodingException {
        updateInitiatorGroup(conn, initiatorPath, groupName, DateraObject.DateraOperation.REMOVE);
    }

    public static Map<String, DateraObject.InitiatorGroup> getAppInstanceInitiatorGroups(
            DateraObject.DateraConnection conn, String appInstance) throws DateraObject.DateraError {
        HttpGet req = new HttpGet(generateApiUrl("app_instances", appInstance, "storage_instances",
                DateraObject.DEFAULT_STORAGE_NAME, "acl_policy", "initiator_groups"));

        String response = executeApiRequest(conn, req);

        if (response == null) {
            return null;
        }

        Type responseType = new TypeToken<Map<String, DateraObject.InitiatorGroup>>() {
        }.getType();

        return gson.fromJson(response, responseType);
    }

    public static void assignGroupToAppInstance(DateraObject.DateraConnection conn, String group,
            String appInstance) throws DateraObject.DateraError, UnsupportedEncodingException {

        DateraObject.InitiatorGroup initiatorGroup = getInitiatorGroup(conn, group);

        if (initiatorGroup == null) {
            throw new CloudRuntimeException("Initator group " + group + " not found ");
        }

        Map<String, DateraObject.InitiatorGroup> initiatorGroups = getAppInstanceInitiatorGroups(conn, appInstance);

        if (initiatorGroups == null) {
            throw new CloudRuntimeException("Initator group not found for appInstnace " + appInstance);
        }

        for (DateraObject.InitiatorGroup ig : initiatorGroups.values()) {
            if (ig.getName().equals(group)) {
                //already assigned
                return;
            }
        }

        HttpPut url = new HttpPut(generateApiUrl("app_instances", appInstance, "storage_instances",
                DateraObject.DEFAULT_STORAGE_NAME, "acl_policy", "initiator_groups"));

        url.setEntity(new StringEntity(gson.toJson(
                new DateraObject.InitiatorGroup(initiatorGroup.getPath(), DateraObject.DateraOperation.ADD))));

        executeApiRequest(conn, url);
    }

    public static void removeGroupFromAppInstance(DateraObject.DateraConnection conn, String group,
            String appInstance) throws DateraObject.DateraError, UnsupportedEncodingException {

        DateraObject.InitiatorGroup initiatorGroup = getInitiatorGroup(conn, group);

        if (initiatorGroup == null) {
            throw new CloudRuntimeException("Initator groups not found for appInstnace " + appInstance);
        }

        Map<String, DateraObject.InitiatorGroup> initiatorGroups = getAppInstanceInitiatorGroups(conn, appInstance);

        if (initiatorGroups == null) {
            throw new CloudRuntimeException("Initator group not found for appInstnace " + appInstance);
        }

        boolean groupAssigned = false;

        for (DateraObject.InitiatorGroup ig : initiatorGroups.values()) {
            if (ig.getName().equals(group)) {
                groupAssigned = true;
                break;
            }
        }

        if (!groupAssigned) {
            return; // already removed
        }

        HttpPut url = new HttpPut(generateApiUrl("app_instances", appInstance, "storage_instances",
                DateraObject.DEFAULT_STORAGE_NAME, "acl_policy", "initiator_groups"));

        url.setEntity(new StringEntity(gson.toJson(
                new DateraObject.InitiatorGroup(initiatorGroup.getPath(), DateraObject.DateraOperation.REMOVE))));

        executeApiRequest(conn, url);
    }

    public static void updateAppInstanceAdminState(DateraObject.DateraConnection conn, String appInstanceName,
            DateraObject.AppState appState) throws UnsupportedEncodingException, DateraObject.DateraError {

        DateraObject.AppInstance appInstance = new DateraObject.AppInstance(appState);
        HttpPut updateAppInstanceReq = new HttpPut(generateApiUrl("app_instances", appInstanceName));

        updateAppInstanceReq.setEntity(new StringEntity(gson.toJson(appInstance)));
        executeApiRequest(conn, updateAppInstanceReq);
    }

    public static void deleteAppInstance(DateraObject.DateraConnection conn, String name)
            throws UnsupportedEncodingException, DateraObject.DateraError {

        HttpDelete deleteAppInstanceReq = new HttpDelete(generateApiUrl("app_instances", name));
        updateAppInstanceAdminState(conn, name, DateraObject.AppState.OFFLINE);
        executeApiRequest(conn, deleteAppInstanceReq);
    }

    public static DateraObject.AppInstance cloneAppInstanceFromSnapshot(DateraObject.DateraConnection conn,
            String newAppInstanceName, String snapshotName)
            throws DateraObject.DateraError, UnsupportedEncodingException {

        //split the snapshot name to appInstanceName and the snapshot timestamp
        String[] tokens = snapshotName.split(":");
        Preconditions.checkArgument(tokens.length == 2);

        // A snapshot is stored in Cloudstack as <AppInstanceName>:<SnapshotTime>
        String appInstanceName = tokens[0];
        String snapshotTime = tokens[1];

        //get the snapshot from Datera
        HttpGet getSnasphotReq = new HttpGet(generateApiUrl("app_instances", appInstanceName, "storage_instances",
                DateraObject.DEFAULT_STORAGE_NAME, "volumes", DateraObject.DEFAULT_VOLUME_NAME, "snapshots",
                snapshotTime));

        String resp = executeApiRequest(conn, getSnasphotReq);

        DateraObject.VolumeSnapshot snapshot = gson.fromJson(resp, DateraObject.VolumeSnapshot.class);

        String snapshotPath = snapshot.getPath();

        DateraObject.AppInstance appInstanceObj = new DateraObject.AppInstance(newAppInstanceName, snapshotPath);

        StringEntity appInstanceEntity = new StringEntity(gson.toJson(appInstanceObj));

        DateraObject.AppInstance appInstance = createAppInstance(conn, newAppInstanceName, appInstanceEntity);

        //bring it online
        updateAppInstanceAdminState(conn, newAppInstanceName, DateraObject.AppState.ONLINE);

        return getAppInstance(conn, newAppInstanceName);
    }

    public static void deleteVolumeSnapshot(DateraObject.DateraConnection conn, String snapshotName)
            throws DateraObject.DateraError {

        // split the snapshot name to appInstanceName and the snapshot timestamp
        String[] tokens = snapshotName.split(":");
        Preconditions.checkArgument(tokens.length == 2);

        // A snapshot is stored in Cloudstack as <AppInstanceName>:<SnapshotTime>
        String appInstanceName = tokens[0];
        String snapshotTime = tokens[1];

        HttpDelete deleteSnapshotReq = new HttpDelete(generateApiUrl("app_instances", appInstanceName,
                "storage_instances", DateraObject.DEFAULT_STORAGE_NAME, "volumes", DateraObject.DEFAULT_VOLUME_NAME,
                "snapshots", snapshotTime));

        executeApiRequest(conn, deleteSnapshotReq);
    }

    public static DateraObject.VolumeSnapshot getVolumeSnapshot(DateraObject.DateraConnection conn,
            String appInstanceName, String snapshotTime) throws DateraObject.DateraError {

        HttpGet getSnapshotReq = new HttpGet(generateApiUrl("app_instances", appInstanceName, "storage_instances",
                DateraObject.DEFAULT_STORAGE_NAME, "volumes", DateraObject.DEFAULT_VOLUME_NAME, "snapshots",
                snapshotTime));

        String resp = executeApiRequest(conn, getSnapshotReq);
        return gson.fromJson(resp, DateraObject.VolumeSnapshot.class);
    }

    public static DateraObject.VolumeSnapshot takeVolumeSnapshot(DateraObject.DateraConnection conn,
            String baseAppInstanceName) throws UnsupportedEncodingException, DateraObject.DateraError {

        HttpPost takeSnasphotReq = new HttpPost(generateApiUrl("app_instances", baseAppInstanceName,
                "storage_instances", DateraObject.DEFAULT_STORAGE_NAME, "volumes", DateraObject.DEFAULT_VOLUME_NAME,
                "snapshots"));

        String snapshotUuid = UUID.randomUUID().toString();
        DateraObject.VolumeSnapshot volumeSnapshot = new DateraObject.VolumeSnapshot(snapshotUuid);
        takeSnasphotReq.setEntity(new StringEntity(gson.toJson(volumeSnapshot)));
        String snapshotResponse = executeApiRequest(conn, takeSnasphotReq);
        volumeSnapshot = gson.fromJson(snapshotResponse, DateraObject.VolumeSnapshot.class);
        String snapshotTime = volumeSnapshot.getTimestamp();

        int retries = DateraUtil.DEFAULT_RETRIES;

        do {
            try {
                Thread.sleep(DateraUtil.POLL_TIMEOUT_MS);
            } catch (InterruptedException e) {
                return null;
            }
            volumeSnapshot = getVolumeSnapshot(conn, baseAppInstanceName, snapshotTime);
        } while ((!Objects.equals(volumeSnapshot.getOpState(), DateraUtil.STATE_AVAILABLE)) && --retries > 0);

        return volumeSnapshot;
    }

    private static String executeApiRequest(DateraObject.DateraConnection conn, HttpRequest apiReq)
            throws DateraObject.DateraError {

        //Get the token first
        String authToken = null;
        try {
            authToken = login(conn);
        } catch (UnsupportedEncodingException e) {
            throw new CloudRuntimeException("Unable to login to Datera " + e.getMessage());
        }

        if (authToken == null) {
            throw new CloudRuntimeException("Unable to login to Datera: error getting auth token ");
        }

        apiReq.addHeader(HEADER_AUTH_TOKEN, authToken);

        return executeHttp(conn, apiReq);
    }

    private static String executeHttp(DateraObject.DateraConnection conn, HttpRequest request)
            throws DateraObject.DateraError {
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        String response = null;

        if (null == httpclient) {
            throw new CloudRuntimeException("Unable to create httpClient for request");
        }

        try {

            request.setHeader(HEADER_CONTENT_TYPE, HEADER_VALUE_JSON);

            HttpHost target = new HttpHost(conn.getManagementIp(), conn.getManagementPort(), SCHEME_HTTP);

            HttpResponse httpResponse = httpclient.execute(target, request);

            HttpEntity entity = httpResponse.getEntity();
            StatusLine status = httpResponse.getStatusLine();
            response = EntityUtils.toString(entity);

            assert response != null;

            if (status.getStatusCode() != HttpStatus.SC_OK) {
                // check if this is an error
                DateraObject.DateraError error = gson.fromJson(response, DateraObject.DateraError.class);
                if (error != null && error.isError()) {
                    throw error;
                } else {
                    throw new CloudRuntimeException("Error while trying to get HTTP object from Datera");
                }

            }

        } catch (IOException e) {
            throw new CloudRuntimeException("Error while sending request to Datera. Error " + e.getMessage());
        }

        return response;
    }

    protected static String generateApiUrl(String... args) {
        ArrayList<String> urlList = new ArrayList<String>(Arrays.asList(args));

        urlList.add(0, API_VERSION);
        urlList.add(0, "");

        return StringUtils.join(urlList, "/");
    }

    public static String getManagementVip(String url) {
        return getVip(DateraUtil.MANAGEMENT_VIP, url);
    }

    public static String getStorageVip(String url) {
        return getVip(DateraUtil.STORAGE_VIP, url);
    }

    public static int getManagementPort(String url) {
        return getPort(DateraUtil.MANAGEMENT_VIP, url, DEFAULT_MANAGEMENT_PORT);
    }

    public static int getStoragePort(String url) {
        return getPort(DateraUtil.STORAGE_VIP, url, DEFAULT_STORAGE_PORT);
    }

    public static int getNumReplicas(String url) {
        try {
            String value = getValue(DateraUtil.NUM_REPLICAS, url, false);
            return Integer.parseInt(value);
        } catch (NumberFormatException ex) {
            return DEFAULT_NUM_REPLICAS;
        }
    }

    private static String getVip(String keyToMatch, String url) {
        String delimiter = ":";

        String storageVip = getValue(keyToMatch, url);

        int index = storageVip.indexOf(delimiter);

        if (index != -1) {
            return storageVip.substring(0, index);
        }

        return storageVip;
    }

    private static int getPort(String keyToMatch, String url, int defaultPortNumber) {
        String delimiter = ":";

        String storageVip = getValue(keyToMatch, url);

        int index = storageVip.indexOf(delimiter);

        int portNumber = defaultPortNumber;

        if (index != -1) {
            String port = storageVip.substring(index + delimiter.length());

            try {
                portNumber = Integer.parseInt(port);
            } catch (NumberFormatException ex) {
                throw new IllegalArgumentException("Invalid URL format (port is not an integer)");
            }
        }

        return portNumber;
    }

    public static String getValue(String keyToMatch, String url) {
        return getValue(keyToMatch, url, true);
    }

    public static String getValue(String keyToMatch, String url, boolean throwExceptionIfNotFound) {
        String delimiter1 = ";";
        String delimiter2 = "=";

        StringTokenizer st = new StringTokenizer(url, delimiter1);

        while (st.hasMoreElements()) {
            String token = st.nextElement().toString();

            int index = token.indexOf(delimiter2);

            if (index == -1) {
                throw new CloudRuntimeException("Invalid URL format");
            }

            String key = token.substring(0, index);

            if (key.equalsIgnoreCase(keyToMatch)) {
                return token.substring(index + delimiter2.length());
            }
        }

        if (throwExceptionIfNotFound) {
            throw new CloudRuntimeException("Key not found in URL");
        }

        return null;
    }

    public static String getModifiedUrl(String originalUrl) {
        StringBuilder sb = new StringBuilder();

        String delimiter = ";";

        StringTokenizer st = new StringTokenizer(originalUrl, delimiter);

        while (st.hasMoreElements()) {
            String token = st.nextElement().toString().toUpperCase();

            if (token.startsWith(DateraUtil.MANAGEMENT_VIP.toUpperCase())
                    || token.startsWith(DateraUtil.STORAGE_VIP.toUpperCase())) {
                sb.append(token).append(delimiter);
            }
        }

        String modifiedUrl = sb.toString();
        int lastIndexOf = modifiedUrl.lastIndexOf(delimiter);

        if (lastIndexOf == (modifiedUrl.length() - delimiter.length())) {
            return modifiedUrl.substring(0, lastIndexOf);
        }

        return modifiedUrl;
    }

    public static DateraObject.DateraConnection getDateraConnection(long storagePoolId,
            StoragePoolDetailsDao storagePoolDetailsDao) {
        StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId,
                DateraUtil.MANAGEMENT_VIP);

        String mVip = storagePoolDetail.getValue();

        storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, DateraUtil.MANAGEMENT_PORT);

        int mPort = Integer.parseInt(storagePoolDetail.getValue());

        storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, DateraUtil.CLUSTER_ADMIN_USERNAME);

        String clusterAdminUsername = storagePoolDetail.getValue();

        storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, DateraUtil.CLUSTER_ADMIN_PASSWORD);

        String clusterAdminPassword = storagePoolDetail.getValue();

        return new DateraObject.DateraConnection(mVip, mPort, clusterAdminUsername, clusterAdminPassword);
    }

    public static boolean hostsSupport_iScsi(List<HostVO> hosts) {
        if (hosts == null || hosts.size() == 0) {
            return false;
        }

        for (Host host : hosts) {
            if (!hostSupport_iScsi(host)) {
                return false;
            }
        }

        return true;
    }

    public static boolean hostSupport_iScsi(Host host) {
        if (host == null || host.getStorageUrl() == null || host.getStorageUrl().trim().length() == 0
                || !host.getStorageUrl().startsWith("iqn")) {
            return false;
        }
        return true;
    }

    public static String getInitiatorGroupKey(long storagePoolId) {
        return "DateraInitiatorGroup-" + storagePoolId;
    }

    /**
     * Checks wether a host initiator is present in an initiator group
     *
     * @param initiator    Host initiator to check
     * @param initiatorGroup the initiator group
     * @return true if host initiator is in the group, false otherwise
     */
    public static boolean isInitiatorPresentInGroup(DateraObject.Initiator initiator,
            DateraObject.InitiatorGroup initiatorGroup) {

        for (String memberPath : initiatorGroup.getMembers()) {
            if (memberPath.equals(initiator.getPath())) {
                return true;
            }
        }

        return false;
    }

    public static int bytesToGb(long volumeSizeBytes) {
        return (int) Math.ceil(volumeSizeBytes / 1073741824.0);
    }

    public static long gbToBytes(int volumeSizeGb) {
        return volumeSizeGb * 1024 * 1024;
    }

    /**
     * IQN path is stored in the DB by cloudstack it is of the form /<IQN>/0
     *
     * @param iqn: IQN of the LUN
     * @return IQN path as defined above
     */
    public static String generateIqnPath(String iqn) {
        if (iqn != null) {
            return "/" + iqn + "/0";
        }
        return null;
    }

    /**
     * Does the opposite of generateIqnPath
     *
     * @param iqnPath
     * @return timmed IQN path
     */

    public static String extractIqn(String iqnPath) {

        if (iqnPath == null) {
            return null;
        }

        if (iqnPath.endsWith("/")) {
            iqnPath = iqnPath.substring(0, iqnPath.length() - 1);
        }

        final String tokens[] = iqnPath.split("/");
        if (tokens.length != 3) {
            final String msg = "Wrong iscsi path " + iqnPath + " it should be /targetIQN/LUN";
            s_logger.warn(msg);
            return null;
        }

        return tokens[1].trim();
    }

}