Blob Blame History Raw
/*
 * Copyright (C) 2011-2014 Red Hat, Inc.
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: tasleson
 */

#include "libstoragemgmt/libstoragemgmt.h"
#include "libstoragemgmt/libstoragemgmt_error.h"
#include "libstoragemgmt/libstoragemgmt_plug_interface.h"
#include "libstoragemgmt/libstoragemgmt_types.h"
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <libxml/uri.h>

#include "lsm_datatypes.hpp"
#include "lsm_convert.hpp"

#define COUNT_OF(x) \
    ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

static const char *const POOL_SEARCH_KEYS[] = { "id", "system_id" };

#define POOL_SEARCH_KEYS_COUNT COUNT_OF(POOL_SEARCH_KEYS)

static const char *const VOLUME_SEARCH_KEYS[] =
    { "id", "system_id", "pool_id" };
#define VOLUME_SEARCH_KEYS_COUNT COUNT_OF(VOLUME_SEARCH_KEYS)

static const char *const DISK_SEARCH_KEYS[] = { "id", "system_id" };

#define DISK_SEARCH_KEYS_COUNT COUNT_OF(DISK_SEARCH_KEYS)

static const char *const FS_SEARCH_KEYS[] = { "id", "system_id", "pool_id" };

#define FS_SEARCH_KEYS_COUNT COUNT_OF(FS_SEARCH_KEYS)

static const char *const NFS_EXPORT_SEARCH_KEYS[] = { "id", "fs_id" };

#define NFS_EXPORT_SEARCH_KEYS_COUNT COUNT_OF(NFS_EXPORT_SEARCH_KEYS)

static const char *const ACCESS_GROUP_SEARCH_KEYS[] = { "id", "system_id" };

#define ACCESS_GROUP_SEARCH_KEYS_COUNT COUNT_OF(ACCESS_GROUP_SEARCH_KEYS)

static const char *const TARGET_PORT_SEARCH_KEYS[] = { "id", "system_id" };

#define TARGET_PORT_SEARCH_KEYS_COUNT COUNT_OF(TARGET_PORT_SEARCH_KEYS)

static int get_battery_array(lsm_connect *c, int rc, Value &response,
                             lsm_battery **bs[], uint32_t *count);

/**
 * Common code to validate and initialize the connection.
 */
#define CONN_SETUP(c)   do {            \
    if(!LSM_IS_CONNECT(c)) {            \
        return LSM_ERR_INVALID_ARGUMENT;\
    }                                   \
    lsm_error_free(c->error);           \
    c->error = NULL;                    \
    } while (0)

static int check_search_key(const char *search_key,
                            const char *const supported_keys[],
                            size_t supported_keys_count)
{
    size_t i = 0;
    for (i = 0; i < supported_keys_count; ++i) {
        if (0 == strcmp(search_key, supported_keys[i])) {
            return 1;
        }
    }
    return 0;
}

int lsm_initiator_id_verify(const char *init_id,
                            lsm_access_group_init_type * init_type)
{
    int rc = LSM_ERR_INVALID_ARGUMENT;

    if (init_id != NULL && strlen(init_id) > 3) {

        switch (*init_type) {
        case (LSM_ACCESS_GROUP_INIT_TYPE_UNKNOWN):
            if (0 == iqn_validate(init_id)) {
                *init_type = LSM_ACCESS_GROUP_INIT_TYPE_ISCSI_IQN;
                rc = LSM_ERR_OK;
            }
            if (0 == wwpn_validate(init_id)) {
                *init_type = LSM_ACCESS_GROUP_INIT_TYPE_WWPN;
                rc = LSM_ERR_OK;
            }
            break;
        case (LSM_ACCESS_GROUP_INIT_TYPE_ISCSI_IQN):
            if (0 == iqn_validate(init_id)) {
                *init_type = LSM_ACCESS_GROUP_INIT_TYPE_ISCSI_IQN;
                rc = LSM_ERR_OK;
            }
            break;
        case (LSM_ACCESS_GROUP_INIT_TYPE_WWPN):
            if (0 == wwpn_validate(init_id)) {
                *init_type = LSM_ACCESS_GROUP_INIT_TYPE_WWPN;
                rc = LSM_ERR_OK;
            }
            break;
        default:
            break;
        }
    }
    return rc;
}

int lsm_volume_vpd83_verify(const char *vpd83)
{
    int rc = LSM_ERR_INVALID_ARGUMENT;
    size_t i;
    size_t vpd83_len;

    if (vpd83) {
        vpd83_len = strlen(vpd83);
        if ((vpd83_len == 32 && vpd83[0] == '6') ||
            (vpd83_len == 16 && vpd83[0] == '2') ||
            (vpd83_len == 16 && vpd83[0] == '3') ||
            (vpd83_len == 16 && vpd83[0] == '5')) {
            for (i = 0; i < vpd83_len; ++i) {
                char v = vpd83[i];
                //  0-9 || a-f is OK
                if (!((v >= 48 && v <= 57) || (v >= 97 && v <= 102))) {
                    return rc;
                }
            }
            rc = LSM_ERR_OK;
        }
    }
    return rc;
}

static int verify_initiator_id(const char *id,
                               lsm_access_group_init_type t, Value & initiator)
{
    initiator = Value(id);

    if (t == LSM_ACCESS_GROUP_INIT_TYPE_WWPN) {
        char *wwpn = wwpn_convert(id);
        if (wwpn) {
            initiator = Value(wwpn);
            free(wwpn);
            wwpn = NULL;
        } else {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    } else if (t == LSM_ACCESS_GROUP_INIT_TYPE_ISCSI_IQN) {
        if (iqn_validate(id)) {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }
    return LSM_ERR_OK;
}

/**
 * Strings are non null with a len >= 1
 */
#define CHECK_STR(x) ( !(x) || !strlen(x) )

/**
 * When we pass in a pointer for an out value we want to make sure that
 * the pointer isn't null, and that the dereferenced value is != NULL to prevent
 * memory leaks.
 */
#define CHECK_RP(x)  (!(x) || *(x) != NULL)

int lsm_connect_password(const char *uri, const char *password,
                         lsm_connect ** conn, uint32_t timeout,
                         lsm_error_ptr * e, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    lsm_connect *c = NULL;

    /* Password is optional */
    if (CHECK_STR(uri) || CHECK_RP(conn) || !timeout || CHECK_RP(e) ||
        LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    c = connection_get();
    if (c) {
        c->uri = xmlParseURI(uri);
        if (c->uri && c->uri->scheme) {
            c->raw_uri = strdup(uri);
            if (c->raw_uri) {
                rc = driver_load(c, c->uri->scheme, password, timeout, e,
                                 1, flags);
                if (rc == LSM_ERR_OK) {
                    *conn = (lsm_connect *) c;
                }
            } else {
                rc = LSM_ERR_NO_MEMORY;
            }
        } else {
            rc = LSM_ERR_INVALID_ARGUMENT;
        }

        /*If we fail for any reason free resources associated with connection */
        if (rc != LSM_ERR_OK) {
            connection_free(c);
        }
    } else {
        rc = LSM_ERR_NO_MEMORY;
    }
    return rc;
}

static int lsm_error_log(lsm_connect * c, lsm_error_ptr error)
{
    if (!LSM_IS_CONNECT(c) || !LSM_IS_ERROR(error)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (c->error) {
        lsm_error_free(c->error);
        c->error = NULL;
    }

    c->error = error;
    return LSM_ERR_OK;
}

static lsm_error_number log_exception(lsm_connect * c,
                                      lsm_error_number error,
                                      const char *message,
                                      const char *exception_msg)
{
    lsm_error_ptr err = lsm_error_create(error, message,
                                         exception_msg, NULL,
                                         NULL, 0);
    if (err) {
        lsm_error_log(c, err);
    }
    return error;
}

static int rpc(lsm_connect * c, const char *method,
               const Value & parameters, Value & response) throw()
{
    try {
        response = c->tp->rpc(method, parameters);
    } catch(const ValueException & ve) {
        return log_exception(c, LSM_ERR_TRANSPORT_SERIALIZATION,
                             "Serialization error", ve.what());
    }
    catch(const LsmException & le) {
        return log_exception(c, (lsm_error_number) le.error_code,
                             le.what(), NULL);
    }
    catch(const EOFException & eof) {
        return log_exception(c, LSM_ERR_TRANSPORT_COMMUNICATION,
                             "Plug-in died", "Check syslog");
    }
    catch( ...) {
        return log_exception(c, LSM_ERR_LIB_BUG, "Unexpected exception",
                             "Unknown exception");
    }
    return LSM_ERR_OK;
}

static int job_check(lsm_connect * c, int rc, Value & response, char **job)
{
    try {
        if (LSM_ERR_OK == rc) {
            //We get a value back, either null or job id.
            if (Value::string_t == response.valueType()) {
                *job = strdup(response.asString().c_str());

                if (*job) {
                    rc = LSM_ERR_JOB_STARTED;
                } else {
                    rc = LSM_ERR_NO_MEMORY;
                }
            } else {
                *job = NULL;
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Wrong type", ve.what());
    }
    return rc;
}

static int get_access_groups(lsm_connect * c, int rc, Value & response,
                             lsm_access_group ** groups[], uint32_t * count)
{
    try {
        if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
            rc = value_array_to_access_groups(response, groups, count);
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

static int add_search_params(std::map < std::string, Value > &p,
                             const char *k, const char *v,
                             const char *const supported_keys[],
                             size_t supported_keys_count)
{
    if (k) {
        if (v) {
            if (!check_search_key(k, supported_keys, supported_keys_count)) {
                return LSM_ERR_UNSUPPORTED_SEARCH_KEY;
            }
        } else {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }
    p["search_key"] = Value(k);
    p["search_value"] = Value(v);
    return LSM_ERR_OK;
}

int lsm_connect_close(lsm_connect * c, lsm_flag flags)
{
    CONN_SETUP(c);

    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["flags"] = Value(flags);
    Value parameters(p);
    Value response;

    //No response data needed on plugin_unregister
    int rc = rpc(c, "plugin_unregister", parameters, response);

    //Free the connection.
    connection_free(c);
    return rc;
}

static Value _create_flag_param(lsm_flag flags)
{
    std::map < std::string, Value > p;
    p["flags"] = Value(flags);
    return Value(p);
}

int lsm_plugin_info_get(lsm_connect * c, char **desc,
                        char **version, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (CHECK_RP(desc) || CHECK_RP(version)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        Value parameters = _create_flag_param(flags);
        Value response;

        rc = rpc(c, "plugin_info", parameters, response);

        if (rc == LSM_ERR_OK) {
            std::vector < Value > j = response.asArray();
            *desc = strdup(j[0].asC_str());
            *version = strdup(j[1].asC_str());

            if (!*desc || !*version) {
                rc = LSM_ERR_NO_MEMORY;
                free(*desc);
                free(*version);
            }
        }
    }
    catch(const ValueException & ve) {
        free(*desc);
        *desc = NULL;
        free(*version);
        *version = NULL;
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }

    return rc;
}

int lsm_available_plugins_list(const char *sep,
                               lsm_string_list ** plugins, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    DIR *dirp = NULL;
    struct dirent *dp = NULL;
    lsm_connect *c = NULL;
    lsm_error_ptr e = NULL;
    char *desc = NULL;
    char *version = NULL;
    char *s = NULL;
    const char *uds_dir = uds_path();
    lsm_string_list *plugin_list = NULL;

    if (CHECK_STR(sep) || CHECK_RP(plugins)
        || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    plugin_list = lsm_string_list_alloc(0);
    if (!plugin_list) {
        return LSM_ERR_NO_MEMORY;
    }

    dirp = opendir(uds_dir);
    if (dirp) {
        for (;;) {
            dp = readdir(dirp);
            if (NULL == dp) {
                break;
            }
            // Check to see if we have a socket
            if (DT_SOCK == dp->d_type) {
                c = connection_get();
                if (c) {
                    rc = driver_load(c, dp->d_name, NULL, 30000, &e, 0, 0);
                    if (LSM_ERR_OK == rc) {
                        // Get the plugin information
                        rc = lsm_plugin_info_get(c, &desc, &version, 0);
                        if (LSM_ERR_OK == rc) {
                            int format =
                                asprintf(&s, "%s%s%s", desc, sep, version);
                            free(desc);
                            desc = NULL;
                            free(version);
                            version = NULL;

                            if (-1 == format) {
                                rc = LSM_ERR_NO_MEMORY;
                                break;
                            }

                            rc = lsm_string_list_append(plugin_list, s);
                            free(s);
                            s = NULL;
                            if (LSM_ERR_OK != rc) {
                                break;
                            }

                        }
                    } else {
                        break;
                    }

                    connection_free(c);
                    c = NULL;
                }
            }
        }                       /* for(;;) */

        if (e) {
            lsm_error_free(e);
            e = NULL;
        }

        if (c) {
            connection_free(c);
            c = NULL;
        }


        if (-1 == closedir(dirp)) {
            //log the error
            rc = LSM_ERR_LIB_BUG;
        }

    } else {                    /* If dirp == NULL */
        //Log the error
        rc = LSM_ERR_LIB_BUG;
    }

    if (LSM_ERR_OK == rc) {
        *plugins = plugin_list;
    } else {
        lsm_string_list_free(plugin_list);
        plugin_list = NULL;
    }

    return rc;
}

int lsm_connect_timeout_set(lsm_connect * c, uint32_t timeout, lsm_flag flags)
{
    CONN_SETUP(c);

    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["ms"] = Value(timeout);
    p["flags"] = Value(flags);
    Value parameters(p);
    Value response;

    //No response data needed on set time out.
    return rpc(c, "time_out_set", parameters, response);
}

int lsm_connect_timeout_get(lsm_connect * c, uint32_t * timeout, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        Value parameters = _create_flag_param(flags);
        Value response;

        rc = rpc(c, "time_out_get", parameters, response);
        if (rc == LSM_ERR_OK) {
            *timeout = response.asUint32_t();
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

static int job_status(lsm_connect * c, const char *job,
                      lsm_job_status * status, uint8_t * percentComplete,
                      Value & returned_value, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!job || !status || !percentComplete) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {
        std::map < std::string, Value > p;
        p["job_id"] = Value(job);
        p["flags"] = Value(flags);
        Value parameters(p);
        Value response;

        rc = rpc(c, "job_status", parameters, response);
        if (LSM_ERR_OK == rc) {
            //We get back an array [status, percent, volume]
            std::vector < Value > j = response.asArray();
            *status = (lsm_job_status) j[0].asInt32_t();
            *percentComplete = (uint8_t) j[1].asUint32_t();

            returned_value = j[2];
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_job_status_get(lsm_connect * c, const char *job_id,
                       lsm_job_status * status, uint8_t * percentComplete,
                       lsm_flag flags)
{
    CONN_SETUP(c);

    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    Value rv;
    return job_status(c, job_id, status, percentComplete, rv, flags);
}

int lsm_job_status_pool_get(lsm_connect * c,
                            const char *job, lsm_job_status * status,
                            uint8_t * percentComplete, lsm_pool ** pool,
                            lsm_flag flags)
{
    Value rv;
    int rc = LSM_ERR_OK;

    CONN_SETUP(c);

    if (CHECK_RP(pool) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        rc = job_status(c, job, status, percentComplete, rv, flags);

        if (LSM_ERR_OK == rc) {
            if (Value::object_t == rv.valueType()) {
                *pool = value_to_pool(rv);
                if (!(*pool)) {
                    rc = LSM_ERR_NO_MEMORY;
                }
            } else {
                *pool = NULL;
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

/* TODO(Gris Ge): Should raise INVALID_ARGUMENT error when specified job does
 *                not a volume return job. The same issue also applies to
 *                lsm_job_status_pool_get() and etc.
 */
int lsm_job_status_volume_get(lsm_connect * c, const char *job,
                              lsm_job_status * status,
                              uint8_t * percentComplete, lsm_volume ** vol,
                              lsm_flag flags)
{
    Value rv;
    int rc = LSM_ERR_OK;

    CONN_SETUP(c);

    if (CHECK_RP(vol) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        rc = job_status(c, job, status, percentComplete, rv, flags);

        if (LSM_ERR_OK == rc) {
            if (Value::object_t == rv.valueType()) {
                *vol = value_to_volume(rv);
                if (!(*vol)) {
                    rc = LSM_ERR_NO_MEMORY;
                }
            } else {
                *vol = NULL;
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_job_status_fs_get(lsm_connect * c, const char *job,
                          lsm_job_status * status,
                          uint8_t * percentComplete, lsm_fs ** fs,
                          lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    Value rv;

    if (CHECK_RP(fs) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        rc = job_status(c, job, status, percentComplete, rv, flags);

        if (LSM_ERR_OK == rc) {
            if (Value::object_t == rv.valueType()) {
                *fs = value_to_fs(rv);
                if (!(*fs)) {
                    rc = LSM_ERR_NO_MEMORY;
                }
            } else {
                *fs = NULL;
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_job_status_ss_get(lsm_connect * c, const char *job,
                          lsm_job_status * status,
                          uint8_t * percentComplete, lsm_fs_ss ** ss,
                          lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    Value rv;

    if (CHECK_RP(ss) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        rc = job_status(c, job, status, percentComplete, rv, flags);

        if (LSM_ERR_OK == rc) {
            if (Value::object_t == rv.valueType()) {
                *ss = value_to_ss(rv);
                if (!(*ss)) {
                    rc = LSM_ERR_NO_MEMORY;
                }
            } else {
                *ss = NULL;
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_job_free(lsm_connect * c, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (job == NULL || strlen(*job) < 1 || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["job_id"] = Value(*job);
    p["flags"] = Value(flags);
    Value parameters(p);
    Value response;

    int rc = rpc(c, "job_free", parameters, response);

    if (LSM_ERR_OK == rc) {
        /* Free the memory for the job id */
        free(*job);
        *job = NULL;
    }
    return rc;
}

int lsm_capabilities(lsm_connect * c, lsm_system * system,
                     lsm_storage_capabilities ** cap, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!LSM_IS_SYSTEM(system) || CHECK_RP(cap) ||
        LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;

    p["system"] = system_to_value(system);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    try {
        rc = rpc(c, "capabilities", parameters, response);

        if (LSM_ERR_OK == rc && Value::object_t == response.valueType()) {
            *cap = value_to_capabilities(response);
            if (!(*cap)) {
                rc = LSM_ERR_NO_MEMORY;
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }

    return rc;
}

int lsm_pool_list(lsm_connect * c, char *search_key, char *search_value,
                  lsm_pool ** poolArray[], uint32_t * count, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!poolArray || !count || CHECK_RP(poolArray)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    *count = 0;
    *poolArray = NULL;

    try {
        std::map < std::string, Value > p;

        rc = add_search_params(p, search_key, search_value,
                               POOL_SEARCH_KEYS, POOL_SEARCH_KEYS_COUNT);
        if (LSM_ERR_OK != rc) {
            return rc;
        }

        p["flags"] = Value(flags);
        Value parameters(p);
        Value response;

        rc = rpc(c, "pools", parameters, response);
        if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
            std::vector < Value > pools = response.asArray();

            *count = pools.size();

            if (pools.size()) {
                *poolArray = lsm_pool_record_array_alloc(pools.size());

                for (size_t i = 0; i < pools.size(); ++i) {
                    (*poolArray)[i] = value_to_pool(pools[i]);
                    if (!(*poolArray)[i]) {
                        rc = LSM_ERR_NO_MEMORY;
                        goto error;
                    }
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
        goto error;
    }

  out:
    return rc;

  error:
    if (*poolArray && *count) {
        lsm_pool_record_array_free(*poolArray, *count);
        *poolArray = NULL;
        *count = 0;
    }
    goto out;
}

int lsm_pool_member_info(lsm_connect * c, lsm_pool * pool,
                         lsm_volume_raid_type * raid_type,
                         lsm_pool_member_type * member_type,
                         lsm_string_list ** member_ids, lsm_flag flags)
{
    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!LSM_IS_POOL(pool)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (!raid_type || !member_type || !member_ids) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["pool"] = pool_to_value(pool);
    p["flags"] = Value(flags);
    Value parameters(p);
    try {

        Value response;

        rc = rpc(c, "pool_member_info", parameters, response);
        if (LSM_ERR_OK == rc) {
            std::vector < Value > j = response.asArray();
            *raid_type = (lsm_volume_raid_type) j[0].asInt32_t();
            *member_type = (lsm_pool_member_type) j[1].asInt32_t();
            *member_ids = NULL;
            if (Value::array_t == j[2].valueType()) {
                if (j[2].asArray().size()) {
                    *member_ids = value_to_string_list(j[2]);
                    if (*member_ids == NULL) {
                        return LSM_ERR_NO_MEMORY;
                    } else if (lsm_string_list_size(*member_ids) !=
                               j[2].asArray().size()) {

                        lsm_string_list_free(*member_ids);
                        return LSM_ERR_NO_MEMORY;
                    }
                }
            } else {
                rc = log_exception(c, LSM_ERR_PLUGIN_BUG,
                                   "member_ids data is not an array",
                                   "member_ids data is not an array");
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;

}

int lsm_target_port_list(lsm_connect * c, const char *search_key,
                         const char *search_value,
                         lsm_target_port ** target_ports[],
                         uint32_t * count, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!target_ports || !count || CHECK_RP(target_ports)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {
        std::map < std::string, Value > p;

        rc = add_search_params(p, search_key, search_value,
                               TARGET_PORT_SEARCH_KEYS,
                               TARGET_PORT_SEARCH_KEYS_COUNT);
        if (LSM_ERR_OK != rc) {
            return rc;
        }

        p["flags"] = Value(flags);
        Value parameters(p);
        Value response;

        rc = rpc(c, "target_ports", parameters, response);
        if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
            std::vector < Value > tp = response.asArray();

            *count = tp.size();

            if (tp.size()) {
                *target_ports = lsm_target_port_record_array_alloc(tp.size());

                for (size_t i = 0; i < tp.size(); ++i) {
                    (*target_ports)[i] = value_to_target_port(tp[i]);
                    if (!((*target_ports)[i])) {
                        rc = LSM_ERR_NO_MEMORY;
                        goto error;
                    }
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
        goto error;
    }
  out:
    return rc;

  error:
    if (*target_ports && *count) {
        lsm_target_port_record_array_free(*target_ports, *count);
        *target_ports = NULL;
        *count = 0;
    }
    goto out;
}

static int get_volume_array(lsm_connect * c, int rc, Value & response,
                            lsm_volume ** volumes[], uint32_t * count)
{
    if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
        try {
            rc = value_array_to_volumes(response, volumes, count);
        }
        catch(const ValueException & ve) {
            rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Wrong type", ve.what());
        }
    }
    return rc;
}


int lsm_volume_list(lsm_connect * c, const char *search_key,
                    const char *search_value, lsm_volume ** volumes[],
                    uint32_t * count, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!volumes || !count || CHECK_RP(volumes)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["flags"] = Value(flags);

    int rc = add_search_params(p, search_key, search_value, VOLUME_SEARCH_KEYS,
                               VOLUME_SEARCH_KEYS_COUNT);
    if (LSM_ERR_OK != rc) {
        return rc;
    }

    Value parameters(p);
    Value response;

    rc = rpc(c, "volumes", parameters, response);
    return get_volume_array(c, rc, response, volumes, count);
}

static int get_disk_array(lsm_connect * c, int rc, Value & response,
                          lsm_disk ** disks[], uint32_t * count)
{
    if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
        rc = value_array_to_disks(response, disks, count);

        if (LSM_ERR_OK != rc) {
            rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", NULL);
        }
    }

    return rc;
}

int lsm_disk_list(lsm_connect * c, const char *search_key,
                  const char *search_value,
                  lsm_disk ** disks[], uint32_t * count, lsm_flag flags)
{
    CONN_SETUP(c);

    if (CHECK_RP(disks) || !count) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["flags"] = Value(flags);

    int rc = add_search_params(p, search_key, search_value, DISK_SEARCH_KEYS,
                               DISK_SEARCH_KEYS_COUNT);
    if (LSM_ERR_OK != rc) {
        return rc;
    }

    Value parameters(p);
    Value response;

    rc = rpc(c, "disks", parameters, response);
    return get_disk_array(c, rc, response, disks, count);
}

typedef void *(*convert) (Value & v);

static void *parse_job_response(lsm_connect * c, Value response, int &rc,
                                char **job, convert conv)
{
    void *val = NULL;
    *job = NULL;

    try {
        //We get an array back. first value is job, second is data of interest.
        if (Value::array_t == response.valueType()) {
            std::vector < Value > r = response.asArray();
            if (Value::string_t == r[0].valueType()) {
                *job = strdup((r[0].asString()).c_str());
                if (!(*job)) {
                    rc = LSM_ERR_NO_MEMORY;
                    goto error;
                }

                rc = LSM_ERR_JOB_STARTED;
            }
            if (Value::object_t == r[1].valueType()) {
                val = conv(r[1]);
                if (!val) {
                    rc = LSM_ERR_NO_MEMORY;
                    goto error;
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
        goto error;
    }

  out:
    return val;

  error:
    free(*job);
    *job = NULL;
    goto out;
}

int lsm_volume_create(lsm_connect * c, lsm_pool * pool,
                      const char *volumeName, uint64_t size,
                      lsm_volume_provision_type provisioning,
                      lsm_volume ** newVolume, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_POOL(pool)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (CHECK_STR(volumeName) || !size || CHECK_RP(newVolume) ||
        CHECK_RP(job) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["pool"] = pool_to_value(pool);
    p["volume_name"] = Value(volumeName);
    p["size_bytes"] = Value(size);
    p["provisioning"] = Value((int32_t) provisioning);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "volume_create", parameters, response);
    if (LSM_ERR_OK == rc) {
        *newVolume =
            (lsm_volume *) parse_job_response(c, response, rc, job,
                                              (convert) value_to_volume);
    }
    return rc;
}

int lsm_volume_resize(lsm_connect * c, lsm_volume * volume,
                      uint64_t newSize, lsm_volume ** resizedVolume,
                      char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_VOL(volume) || !newSize || CHECK_RP(resizedVolume) ||
        CHECK_RP(job) || newSize == 0 || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }
    //If you try to resize to same size, we will return error.
    if ((newSize / volume->block_size) == volume->number_of_blocks) {
        return LSM_ERR_NO_STATE_CHANGE;
    }

    std::map < std::string, Value > p;
    p["volume"] = volume_to_value(volume);
    p["new_size_bytes"] = Value(newSize);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "volume_resize", parameters, response);
    if (LSM_ERR_OK == rc) {
        *resizedVolume =
            (lsm_volume *) parse_job_response(c, response, rc, job,
                                              (convert) value_to_volume);
    }
    return rc;
}

int lsm_volume_replicate(lsm_connect * c, lsm_pool * pool,
                         lsm_replication_type repType,
                         lsm_volume * volumeSrc, const char *name,
                         lsm_volume ** newReplicant, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if ((pool && !LSM_IS_POOL(pool)) || !LSM_IS_VOL(volumeSrc)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (CHECK_STR(name) || CHECK_RP(newReplicant) || CHECK_RP(job) ||
        LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["pool"] = pool_to_value(pool);
    p["rep_type"] = Value((int32_t) repType);
    p["volume_src"] = volume_to_value(volumeSrc);
    p["name"] = Value(name);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "volume_replicate", parameters, response);
    if (LSM_ERR_OK == rc) {
        *newReplicant =
            (lsm_volume *) parse_job_response(c, response, rc, job,
                                              (convert) value_to_volume);
    }
    return rc;

}

int lsm_volume_replicate_range_block_size(lsm_connect * c,
                                          lsm_system * system,
                                          uint32_t * bs, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!bs || LSM_FLAG_UNUSED_CHECK(flags) || !LSM_IS_SYSTEM(system)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {
        std::map < std::string, Value > p;
        p["system"] = system_to_value(system);
        p["flags"] = Value(flags);
        Value parameters(p);
        Value response;

        rc = rpc(c, "volume_replicate_range_block_size", parameters, response);
        if (LSM_ERR_OK == rc) {
            if (Value::numeric_t == response.valueType()) {
                *bs = response.asUint32_t();
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}


int lsm_volume_replicate_range(lsm_connect * c,
                               lsm_replication_type repType,
                               lsm_volume * source,
                               lsm_volume * dest,
                               lsm_block_range ** ranges,
                               uint32_t num_ranges, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_VOL(source) || !LSM_IS_VOL(dest)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (!ranges || !num_ranges || CHECK_RP(job) ||
        LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["rep_type"] = Value((int32_t) repType);
    p["volume_src"] = volume_to_value(source);
    p["volume_dest"] = volume_to_value(dest);
    p["ranges"] = block_range_list_to_value(ranges, num_ranges);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "volume_replicate_range", parameters, response);
    return job_check(c, rc, response, job);
}

static Value _create_volume_flag_param(lsm_volume * volume, lsm_flag flags)
{
    std::map < std::string, Value > p;
    p["volume"] = volume_to_value(volume);
    p["flags"] = Value(flags);

    return Value(p);
}

int lsm_volume_delete(lsm_connect * c, lsm_volume * volume, char **job,
                      lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!LSM_IS_VOL(volume)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (CHECK_RP(job) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        Value parameters = _create_volume_flag_param(volume, flags);
        Value response;

        rc = rpc(c, "volume_delete", parameters, response);
        rc = job_check(c, rc, response, job);
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;

}

int lsm_volume_raid_info(lsm_connect * c, lsm_volume * volume,
                         lsm_volume_raid_type * raid_type,
                         uint32_t * strip_size, uint32_t * disk_count,
                         uint32_t * min_io_size, uint32_t * opt_io_size,
                         lsm_flag flags)
{
    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!LSM_IS_VOL(volume)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (!raid_type || !strip_size || !disk_count || !min_io_size ||
        !opt_io_size) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        Value parameters = _create_volume_flag_param(volume, flags);
        Value response;

        rc = rpc(c, "volume_raid_info", parameters, response);
        if (LSM_ERR_OK == rc) {
            //We get a value back, either null or job id.
            std::vector < Value > j = response.asArray();
            *raid_type = (lsm_volume_raid_type) j[0].asInt32_t();
            *strip_size = j[1].asUint32_t();
            *disk_count = j[2].asUint32_t();
            *min_io_size = j[3].asUint32_t();
            *opt_io_size = j[4].asUint32_t();
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;

}

int lsm_iscsi_chap_auth(lsm_connect * c, const char *init_id,
                        const char *username, const char *password,
                        const char *out_user, const char *out_password,
                        lsm_flag flags)
{
    CONN_SETUP(c);

    if (iqn_validate(init_id) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["init_id"] = Value(init_id);
    p["in_user"] = Value(username);
    p["in_password"] = Value(password);
    p["out_user"] = Value(out_user);
    p["out_password"] = Value(out_password);
    p["flags"] = Value(flags);


    Value parameters(p);
    Value response;

    return rpc(c, "iscsi_chap_auth", parameters, response);
}

static int online_offline(lsm_connect * c, lsm_volume * v,
                          const char *operation, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_VOL(v)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["volume"] = volume_to_value(v);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;
    return rpc(c, operation, parameters, response);
}

int lsm_volume_enable(lsm_connect * c, lsm_volume * volume, lsm_flag flags)
{
    return online_offline(c, volume, "volume_enable", flags);
}

int lsm_volume_disable(lsm_connect * c, lsm_volume * volume, lsm_flag flags)
{
    return online_offline(c, volume, "volume_disable", flags);
}

int lsm_access_group_list(lsm_connect * c, const char *search_key,
                          const char *search_value,
                          lsm_access_group ** groups[],
                          uint32_t * groupCount, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!groups || !groupCount) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;

    int rc = add_search_params(p, search_key, search_value,
                               ACCESS_GROUP_SEARCH_KEYS,
                               ACCESS_GROUP_SEARCH_KEYS_COUNT);
    if (LSM_ERR_OK != rc) {
        return rc;
    }

    p["flags"] = Value(flags);
    Value parameters(p);
    Value response;

    rc = rpc(c, "access_groups", parameters, response);
    return get_access_groups(c, rc, response, groups, groupCount);
}

int lsm_access_group_create(lsm_connect * c, const char *name,
                            const char *init_id,
                            lsm_access_group_init_type init_type,
                            lsm_system * system,
                            lsm_access_group ** access_group, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_SYSTEM(system) || CHECK_STR(name) || CHECK_STR(init_id) ||
        CHECK_RP(access_group) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    Value id;

    if (LSM_ERR_OK != verify_initiator_id(init_id, init_type, id)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["name"] = Value(name);
    p["init_id"] = id;
    p["init_type"] = Value((int32_t) init_type);
    p["system"] = system_to_value(system);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    *access_group = NULL;

    int rc = rpc(c, "access_group_create", parameters, response);
    try {
        if (LSM_ERR_OK == rc) {
            //We should be getting a value back.
            if (Value::object_t == response.valueType()) {
                *access_group = value_to_access_group(response);
                if (!(*access_group)) {
                    rc = LSM_ERR_NO_MEMORY;
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_access_group_delete(lsm_connect * c,
                            lsm_access_group * access_group, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_ACCESS_GROUP(access_group) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["access_group"] = access_group_to_value(access_group);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    return rpc(c, "access_group_delete", parameters, response);
}

static int _lsm_ag_add_delete(lsm_connect * c,
                              lsm_access_group * access_group,
                              const char *init_id,
                              lsm_access_group_init_type init_type,
                              lsm_access_group ** updated_access_group,
                              lsm_flag flags, const char *message)
{
    CONN_SETUP(c);

    if (!LSM_IS_ACCESS_GROUP(access_group) || CHECK_STR(init_id) ||
        LSM_FLAG_UNUSED_CHECK(flags) || CHECK_RP(updated_access_group)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    Value id;

    if (LSM_ERR_OK != verify_initiator_id(init_id, init_type, id)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["access_group"] = access_group_to_value(access_group);
    p["init_id"] = id;
    p["init_type"] = Value((int32_t) init_type);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, message, parameters, response);
    try {
        if (LSM_ERR_OK == rc) {
            //We should be getting a value back.
            if (Value::object_t == response.valueType()) {
                *updated_access_group = value_to_access_group(response);
                if (!(*updated_access_group)) {
                    rc = LSM_ERR_NO_MEMORY;
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }

    return rc;
}

int lsm_access_group_initiator_add(lsm_connect * c,
                                   lsm_access_group * access_group,
                                   const char *init_id,
                                   lsm_access_group_init_type init_type,
                                   lsm_access_group **
                                   updated_access_group, lsm_flag flags)
{
    return _lsm_ag_add_delete(c, access_group, init_id, init_type,
                              updated_access_group, flags,
                              "access_group_initiator_add");
}

int lsm_access_group_initiator_delete(lsm_connect * c,
                                      lsm_access_group * access_group,
                                      const char *init_id,
                                      lsm_access_group_init_type init_type,
                                      lsm_access_group **
                                      updated_access_group, lsm_flag flags)
{
    return _lsm_ag_add_delete(c, access_group, init_id, init_type,
                              updated_access_group, flags,
                              "access_group_initiator_delete");
}

int lsm_volume_mask(lsm_connect * c, lsm_access_group * access_group,
                    lsm_volume * volume, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_ACCESS_GROUP(access_group) || !LSM_IS_VOL(volume) ||
        LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["access_group"] = access_group_to_value(access_group);
    p["volume"] = volume_to_value(volume);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    return rpc(c, "volume_mask", parameters, response);
}


int lsm_volume_unmask(lsm_connect * c, lsm_access_group * group,
                      lsm_volume * volume, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_ACCESS_GROUP(group) || !LSM_IS_VOL(volume) ||
        LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["access_group"] = access_group_to_value(group);
    p["volume"] = volume_to_value(volume);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    return rpc(c, "volume_unmask", parameters, response);
}

int lsm_volumes_accessible_by_access_group(lsm_connect * c,
                                           lsm_access_group * group,
                                           lsm_volume ** volumes[],
                                           uint32_t * count, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!LSM_IS_ACCESS_GROUP(group) ||
        !volumes || !count || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {
        std::map < std::string, Value > p;
        p["access_group"] = access_group_to_value(group);
        p["flags"] = Value(flags);

        Value parameters(p);
        Value response;

        rc = rpc(c, "volumes_accessible_by_access_group", parameters, response);
        if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
            std::vector < Value > vol = response.asArray();

            *count = vol.size();

            if (vol.size()) {
                *volumes = lsm_volume_record_array_alloc(vol.size());

                if (*volumes) {
                    for (size_t i = 0; i < vol.size(); ++i) {
                        (*volumes)[i] = value_to_volume(vol[i]);
                        if (!((*volumes)[i])) {
                            rc = LSM_ERR_NO_MEMORY;
                            goto error;
                        }
                    }
                } else {
                    rc = LSM_ERR_NO_MEMORY;
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
        goto error;

    }

  out:
    return rc;

  error:
    if (*volumes && *count) {
        lsm_volume_record_array_free(*volumes, *count);
        *volumes = NULL;
        *count = 0;
    }
    goto out;
}

int lsm_access_groups_granted_to_volume(lsm_connect * c,
                                        lsm_volume * volume,
                                        lsm_access_group ** groups[],
                                        uint32_t * groupCount, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_VOL(volume)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (!groups || !groupCount || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["volume"] = volume_to_value(volume);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "access_groups_granted_to_volume", parameters, response);
    return get_access_groups(c, rc, response, groups, groupCount);
}

static int _retrieve_bool(int rc, Value & response, uint8_t * yes)
{
    int rc_out = rc;

    *yes = 0;

    if (LSM_ERR_OK == rc) {
        //We should be getting a boolean value back.
        if (Value::boolean_t == response.valueType()) {
            if (response.asBool()) {
                *yes = 1;
            }
        } else {
            rc_out = LSM_ERR_PLUGIN_BUG;
        }
    }
    return rc_out;
}

int lsm_volume_child_dependency(lsm_connect * c, lsm_volume * volume,
                                uint8_t * yes, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!LSM_IS_VOL(volume)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (!yes || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        Value parameters = _create_volume_flag_param(volume, flags);
        Value response;

        rc = rpc(c, "volume_child_dependency", parameters, response);
        rc = _retrieve_bool(rc, response, yes);
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_volume_child_dependency_delete(lsm_connect * c,
                                       lsm_volume * volume, char **job,
                                       lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_VOL(volume) || CHECK_RP(job)
        || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    Value parameters = _create_volume_flag_param(volume, flags);
    Value response;

    int rc = rpc(c, "volume_child_dependency_rm", parameters, response);
    return job_check(c, rc, response, job);
}

int lsm_system_list(lsm_connect * c, lsm_system ** systems[],
                    uint32_t * systemCount, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!systems || !systemCount) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {
        std::map < std::string, Value > p;
        p["flags"] = Value(flags);
        Value parameters(p);
        Value response;

        rc = rpc(c, "systems", parameters, response);
        if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
            std::vector < Value > sys = response.asArray();

            *systemCount = sys.size();

            if (sys.size()) {
                *systems = lsm_system_record_array_alloc(sys.size());

                if (*systems) {
                    for (size_t i = 0; i < sys.size(); ++i) {
                        (*systems)[i] = value_to_system(sys[i]);
                        if (!(*systems)[i]) {
                            rc = LSM_ERR_NO_MEMORY;
                            goto error;
                        }
                    }
                } else {
                    rc = LSM_ERR_NO_MEMORY;
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
        goto error;

    }
  out:
    return rc;

  error:
    if (*systems) {
        lsm_system_record_array_free(*systems, *systemCount);
        *systems = NULL;
        *systemCount = 0;
    }
    goto out;
}

int lsm_fs_list(lsm_connect * c, const char *search_key,
                const char *search_value, lsm_fs ** fs[],
                uint32_t * fsCount, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!fs || !fsCount) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {
        std::map < std::string, Value > p;

        rc = add_search_params(p, search_key, search_value, FS_SEARCH_KEYS,
                               FS_SEARCH_KEYS_COUNT);
        if (LSM_ERR_OK != rc) {
            return rc;
        }

        p["flags"] = Value(flags);
        Value parameters(p);
        Value response;

        rc = rpc(c, "fs", parameters, response);
        if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
            std::vector < Value > sys = response.asArray();

            *fsCount = sys.size();

            if (sys.size()) {
                *fs = lsm_fs_record_array_alloc(sys.size());

                if (*fs) {
                    for (size_t i = 0; i < sys.size(); ++i) {
                        (*fs)[i] = value_to_fs(sys[i]);
                        if (!((*fs)[i])) {
                            rc = LSM_ERR_NO_MEMORY;
                            goto error;
                        }
                    }
                } else {
                    rc = LSM_ERR_NO_MEMORY;
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
        goto error;

    }
  out:
    return rc;

  error:
    if (*fs && *fsCount) {
        lsm_fs_record_array_free(*fs, *fsCount);
        *fs = NULL;
        *fsCount = 0;
    }
    goto out;
}

int lsm_fs_create(lsm_connect * c, lsm_pool * pool, const char *name,
                  uint64_t size_bytes, lsm_fs ** fs, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_POOL(pool)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (CHECK_STR(name) || !size_bytes || CHECK_RP(fs) || CHECK_RP(job)
        || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["pool"] = pool_to_value(pool);
    p["name"] = Value(name);
    p["size_bytes"] = Value(size_bytes);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "fs_create", parameters, response);
    if (LSM_ERR_OK == rc) {
        *fs = (lsm_fs *) parse_job_response(c, response, rc, job,
                                            (convert) value_to_fs);
    }
    return rc;
}

static Value _create_fs_flag_param(lsm_fs * fs, lsm_flag flags)
{
    std::map < std::string, Value > p;
    p["fs"] = fs_to_value(fs);
    p["flags"] = Value(flags);
    return Value(p);
}

int lsm_fs_delete(lsm_connect * c, lsm_fs * fs, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs) || CHECK_RP(job) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    Value parameters = _create_fs_flag_param(fs, flags);
    Value response;

    int rc = rpc(c, "fs_delete", parameters, response);
    return job_check(c, rc, response, job);
}


int lsm_fs_resize(lsm_connect * c, lsm_fs * fs,
                  uint64_t new_size_bytes, lsm_fs ** rfs,
                  char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs) || !new_size_bytes || CHECK_RP(rfs) || CHECK_RP(job)
        || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["fs"] = fs_to_value(fs);
    p["new_size_bytes"] = Value(new_size_bytes);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "fs_resize", parameters, response);
    if (LSM_ERR_OK == rc) {
        *rfs = (lsm_fs *) parse_job_response(c, response, rc, job,
                                             (convert) value_to_fs);
    }
    return rc;
}

int lsm_fs_clone(lsm_connect * c, lsm_fs * src_fs,
                 const char *name, lsm_fs_ss * optional_ss,
                 lsm_fs ** cloned_fs, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_FS(src_fs) || CHECK_STR(name) || CHECK_RP(cloned_fs) ||
        CHECK_RP(job) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["src_fs"] = fs_to_value(src_fs);
    p["dest_fs_name"] = Value(name);
    p["snapshot"] = ss_to_value(optional_ss);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "fs_clone", parameters, response);
    if (LSM_ERR_OK == rc) {
        *cloned_fs = (lsm_fs *) parse_job_response(c, response, rc, job,
                                                   (convert) value_to_fs);
    }
    return rc;
}

int lsm_fs_file_clone(lsm_connect * c, lsm_fs * fs,
                      const char *src_file_name,
                      const char *dest_file_name, lsm_fs_ss * snapshot,
                      char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs) || CHECK_STR(src_file_name)
        || CHECK_STR(dest_file_name)
        || CHECK_RP(job) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["fs"] = fs_to_value(fs);
    p["src_file_name"] = Value(src_file_name);
    p["dest_file_name"] = Value(dest_file_name);
    p["snapshot"] = ss_to_value(snapshot);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "fs_file_clone", parameters, response);
    return job_check(c, rc, response, job);
}

static Value _create_fs_file_flag_params(lsm_fs * fs,
                                         lsm_string_list * files,
                                         lsm_flag flags)
{
    std::map < std::string, Value > p;
    p["fs"] = fs_to_value(fs);
    p["files"] = string_list_to_value(files);
    p["flags"] = Value(flags);
    return Value(p);
}

int lsm_fs_child_dependency(lsm_connect * c, lsm_fs * fs,
                            lsm_string_list * files, uint8_t * yes,
                            lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (files) {
        if (!LSM_IS_STRING_LIST(files)) {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }

    if (!yes || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    try {

        Value parameters = _create_fs_file_flag_params(fs, files, flags);
        Value response;

        rc = rpc(c, "fs_child_dependency", parameters, response);
        rc = _retrieve_bool(rc, response, yes);
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_fs_child_dependency_delete(lsm_connect * c, lsm_fs * fs,
                                   lsm_string_list * files, char **job,
                                   lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (files) {
        if (!LSM_IS_STRING_LIST(files)) {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }

    if (CHECK_RP(job) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    Value parameters = _create_fs_file_flag_params(fs, files, flags);
    Value response;

    int rc = rpc(c, "fs_child_dependency_rm", parameters, response);
    return job_check(c, rc, response, job);
}

int lsm_fs_ss_list(lsm_connect * c, lsm_fs * fs, lsm_fs_ss ** ss[],
                   uint32_t * ssCount, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (CHECK_RP(ss) || !ssCount) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    Value parameters = _create_fs_flag_param(fs, flags);
    Value response;

    try {
        rc = rpc(c, "fs_snapshots", parameters, response);
        if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
            std::vector < Value > sys = response.asArray();

            *ssCount = sys.size();

            if (sys.size()) {
                *ss = lsm_fs_ss_record_array_alloc(sys.size());

                if (*ss) {
                    for (size_t i = 0; i < sys.size(); ++i) {
                        (*ss)[i] = value_to_ss(sys[i]);
                        if (!((*ss)[i])) {
                            rc = LSM_ERR_NO_MEMORY;
                            goto error;
                        }
                    }
                } else {
                    rc = LSM_ERR_NO_MEMORY;
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
        goto error;
    }
  out:
    return rc;

  error:
    if (*ss && *ssCount) {
        lsm_fs_ss_record_array_free(*ss, *ssCount);
        *ss = NULL;
        *ssCount = 0;
    }

    goto out;
}

int lsm_fs_ss_create(lsm_connect * c, lsm_fs * fs, const char *name,
                     lsm_fs_ss ** snapshot, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (CHECK_STR(name) || CHECK_RP(snapshot) || CHECK_RP(job)
        || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["fs"] = fs_to_value(fs);
    p["snapshot_name"] = Value(name);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "fs_snapshot_create", parameters, response);
    if (LSM_ERR_OK == rc) {
        *snapshot = (lsm_fs_ss *) parse_job_response(c, response, rc, job,
                                                     (convert)
                                                     value_to_ss);
    }
    return rc;
}

int lsm_fs_ss_delete(lsm_connect * c, lsm_fs * fs, lsm_fs_ss * ss,
                     char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (!LSM_IS_SS(ss)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (CHECK_RP(job) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["fs"] = fs_to_value(fs);
    p["snapshot"] = ss_to_value(ss);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "fs_snapshot_delete", parameters, response);
    return job_check(c, rc, response, job);
}

int lsm_fs_ss_restore(lsm_connect * c, lsm_fs * fs, lsm_fs_ss * ss,
                      lsm_string_list * files,
                      lsm_string_list * restore_files,
                      int all_files, char **job, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_FS(fs)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (!LSM_IS_SS(ss)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    if (files) {
        if (!LSM_IS_STRING_LIST(files)) {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }

    if (restore_files) {
        if (!LSM_IS_STRING_LIST(restore_files)) {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }

    if (CHECK_RP(job) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["fs"] = fs_to_value(fs);
    p["snapshot"] = ss_to_value(ss);
    p["files"] = string_list_to_value(files);
    p["restore_files"] = string_list_to_value(restore_files);
    p["all_files"] = Value((all_files) ? true : false);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "fs_snapshot_restore", parameters, response);
    return job_check(c, rc, response, job);

}

int lsm_nfs_list(lsm_connect * c, const char *search_key,
                 const char *search_value, lsm_nfs_export ** exports[],
                 uint32_t * count, lsm_flag flags)
{
    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if (CHECK_RP(exports) || !count) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    *count = 0;
    *exports = NULL;

    try {
        std::map < std::string, Value > p;

        rc = add_search_params(p, search_key, search_value,
                               NFS_EXPORT_SEARCH_KEYS,
                               NFS_EXPORT_SEARCH_KEYS_COUNT);
        if (LSM_ERR_OK != rc) {
            return rc;
        }

        p["flags"] = Value(flags);
        Value parameters(p);
        Value response;

        rc = rpc(c, "exports", parameters, response);
        if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
            std::vector < Value > exps = response.asArray();

            *count = exps.size();

            if (*count) {
                *exports = lsm_nfs_export_record_array_alloc(*count);

                if (*exports) {
                    for (size_t i = 0; i < *count; ++i) {
                        (*exports)[i] = value_to_nfs_export(exps[i]);
                        if (!((*exports)[i])) {
                            rc = LSM_ERR_NO_MEMORY;
                            goto error;
                        }
                    }
                } else {
                    rc = LSM_ERR_NO_MEMORY;
                }
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
        goto error;
    }
  out:
    return rc;

  error:
    if (*exports && *count) {
        lsm_nfs_export_record_array_free(*exports, *count);
        *exports = NULL;
        *count = 0;
    }
    goto out;
}

int lsm_nfs_export_fs(lsm_connect * c,
                      const char *fs_id,
                      const char *export_path,
                      lsm_string_list * root_list,
                      lsm_string_list * rw_list,
                      lsm_string_list * ro_list,
                      uint64_t anon_uid,
                      uint64_t anon_gid,
                      const char *auth_type,
                      const char *options,
                      lsm_nfs_export ** exported, lsm_flag flags)
{
    CONN_SETUP(c);

    if (root_list) {
        if (!LSM_IS_STRING_LIST(root_list)) {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }

    if (rw_list) {
        if (!LSM_IS_STRING_LIST(rw_list)) {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }

    if (ro_list) {
        if (!LSM_IS_STRING_LIST(ro_list)) {
            return LSM_ERR_INVALID_ARGUMENT;
        }
    }

    if (CHECK_STR(fs_id) || CHECK_RP(exported)
        || !(root_list || rw_list || ro_list)
        || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;

    p["fs_id"] = Value(fs_id);
    p["export_path"] = Value(export_path);
    p["root_list"] = string_list_to_value(root_list);
    p["rw_list"] = string_list_to_value(rw_list);
    p["ro_list"] = string_list_to_value(ro_list);
    if ((int64_t) anon_uid == LSM_NFS_EXPORT_ANON_UID_GID_NA)
        p["anon_uid"] = Value(LSM_NFS_EXPORT_ANON_UID_GID_NA);
    else if ((int64_t) anon_uid == LSM_NFS_EXPORT_ANON_UID_GID_ERROR)
        p["anon_uid"] = Value(LSM_NFS_EXPORT_ANON_UID_GID_ERROR);
    else
        p["anon_uid"] = Value(anon_uid);
    if ((int64_t) anon_gid == LSM_NFS_EXPORT_ANON_UID_GID_NA)
        p["anon_gid"] = Value(LSM_NFS_EXPORT_ANON_UID_GID_NA);
    else if ((int64_t) anon_gid == LSM_NFS_EXPORT_ANON_UID_GID_ERROR)
        p["anon_gid"] = Value(LSM_NFS_EXPORT_ANON_UID_GID_ERROR);
    else
        p["anon_gid"] = Value(anon_gid);
    p["auth_type"] = Value(auth_type);
    p["options"] = Value(options);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "export_fs", parameters, response);
    try {
        if (LSM_ERR_OK == rc && Value::object_t == response.valueType()) {
            *exported = value_to_nfs_export(response);
            if (!(*exported)) {
                rc = LSM_ERR_NO_MEMORY;
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_nfs_export_delete(lsm_connect * c, lsm_nfs_export * e, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_NFS_EXPORT(e) || LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["export"] = nfs_export_to_value(e);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "export_remove", parameters, response);
    return rc;
}

int lsm_volume_raid_create_cap_get(lsm_connect * c, lsm_system * system,
                                   uint32_t ** supported_raid_types,
                                   uint32_t * supported_raid_type_count,
                                   uint32_t ** supported_strip_sizes,
                                   uint32_t * supported_strip_size_count,
                                   lsm_flag flags)
{
    CONN_SETUP(c);

    if (!supported_raid_types || !supported_raid_type_count ||
        !supported_strip_sizes || !supported_strip_size_count) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    *supported_raid_types = NULL;

    std::map < std::string, Value > p;
    p["system"] = system_to_value(system);
    p["flags"] = Value(flags);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "volume_raid_create_cap_get", parameters, response);
    try {
        std::vector < Value > j = response.asArray();

        rc = values_to_uint32_array(j[0], supported_raid_types,
                                    supported_raid_type_count);

        if (rc != LSM_ERR_OK) {
            goto error;
        }

        rc = values_to_uint32_array(j[1], supported_strip_sizes,
                                    supported_strip_size_count);
        if (rc != LSM_ERR_OK) {
            goto error;
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }

  out:
    return rc;

  error:
    free(*supported_raid_types);
    *supported_raid_types = NULL;
    *supported_raid_type_count = 0;
    *supported_strip_sizes = NULL;
    *supported_strip_size_count = 0;
    goto out;
}

int lsm_volume_raid_create(lsm_connect * c, const char *name,
                           lsm_volume_raid_type raid_type,
                           lsm_disk * disks[], uint32_t disk_count,
                           uint32_t strip_size, lsm_volume ** new_volume,
                           lsm_flag flags)
{
    CONN_SETUP(c);

    if (disk_count == 0) {
        return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                             "Require at least one disks", NULL);
    }

    if (raid_type == LSM_VOLUME_RAID_TYPE_RAID1 && disk_count != 2) {
        return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                             "RAID 1 only allows two disks", NULL);
    }

    if (raid_type == LSM_VOLUME_RAID_TYPE_RAID5 && disk_count < 3) {
        return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                             "RAID 5 require 3 or more disks", NULL);
    }

    if (raid_type == LSM_VOLUME_RAID_TYPE_RAID6 && disk_count < 4) {
        return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                             "RAID 5 require 4 or more disks", NULL);
    }

    if (disk_count % 2) {
        if (raid_type == LSM_VOLUME_RAID_TYPE_RAID10 && disk_count < 4) {
            return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                                 "RAID 10 require even disks count and 4 "
                                 "or more disks",
                                 NULL);
        }
        if (raid_type == LSM_VOLUME_RAID_TYPE_RAID50 && disk_count < 6) {
            return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                                 "RAID 50 require even disks count and 6 or "
                                 "more disks",
                                 NULL);
        }
        if (raid_type == LSM_VOLUME_RAID_TYPE_RAID60 && disk_count < 8) {
            return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                                 "RAID 60 require even disks count and 8 or "
                                 "more disks",
                                 NULL);
        }
    }

    if (CHECK_RP(new_volume)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["name"] = Value(name);
    p["raid_type"] = Value((int32_t) raid_type);
    p["strip_size"] = Value((int32_t) strip_size);
    p["flags"] = Value(flags);
    std::vector < Value > disks_value;
    disks_value.reserve(disk_count);
    for (uint32_t i = 0; i < disk_count; i++) {
        disks_value.push_back(disk_to_value(disks[i]));
    }
    p["disks"] = disks_value;

    Value parameters(p);
    Value response;

    int rc = rpc(c, "volume_raid_create", parameters, response);
    try {
        if (LSM_ERR_OK == rc) {
            *new_volume = value_to_volume(response);
            if (!(*new_volume)) {
                rc = LSM_ERR_NO_MEMORY;
            }
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }
    return rc;
}

int lsm_volume_ident_led_on(lsm_connect * c, lsm_volume * volume,
                            lsm_flag flags)
{
    CONN_SETUP(c);

    std::map < std::string, Value > p;
    p["flags"] = Value(flags);
    p["volume"] = volume_to_value(volume);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "volume_ident_led_on", parameters, response);

    return rc;
}

int lsm_volume_ident_led_off(lsm_connect * c, lsm_volume * volume,
                             lsm_flag flags)
{
    CONN_SETUP(c);

    std::map < std::string, Value > p;
    p["flags"] = Value(flags);
    p["volume"] = volume_to_value(volume);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "volume_ident_led_off", parameters, response);

    return rc;
}

int lsm_system_read_cache_pct_update(lsm_connect * c, lsm_system * system,
                                     uint32_t read_pct, lsm_flag flags)
{
    CONN_SETUP(c);

    if (!LSM_IS_SYSTEM(system))
        return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                             "Invalid argument: system", NULL);

    if (LSM_FLAG_UNUSED_CHECK(flags))
        return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                             "Invalid argument: flags", NULL);

    if (read_pct > 100)
        return log_exception(c, LSM_ERR_INVALID_ARGUMENT,
                             "Invalid argument: read_pct, "
                             "should >=0 and <= 100", NULL);

    std::map < std::string, Value > p;
    p["flags"] = Value(flags);
    p["read_pct"] = Value((int32_t) read_pct);
    p["system"] = system_to_value(system);

    Value parameters(p);
    Value response;

    int rc = rpc(c, "system_read_cache_pct_update", parameters, response);

    return rc;
}

static int get_battery_array(lsm_connect *c, int rc, Value &response,
                             lsm_battery **bs[], uint32_t *count)
{
    if (LSM_ERR_OK == rc && Value::array_t == response.valueType()) {
        try {
            rc = value_array_to_batteries(response, bs, count);
        }
        catch(const ValueException & ve) {
            rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", NULL);
        }
    }

    return rc;
}

int lsm_battery_list(lsm_connect *c, const char *search_key,
                     const char *search_value, lsm_battery **bs[],
                     uint32_t *count, lsm_flag flags)
{
    CONN_SETUP(c);

    if (CHECK_RP(bs) || !count) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["flags"] = Value(flags);

    int rc = add_search_params(p, search_key, search_value, DISK_SEARCH_KEYS,
                               DISK_SEARCH_KEYS_COUNT);
    if (LSM_ERR_OK != rc) {
        return rc;
    }

    Value parameters(p);
    Value response;

    rc = rpc(c, "batteries", parameters, response);
    return get_battery_array(c, rc, response, bs, count);
}

int lsm_volume_cache_info(lsm_connect *c, lsm_volume *volume,
                          uint32_t *write_cache_policy,
                          uint32_t *write_cache_status,
                          uint32_t *read_cache_policy,
                          uint32_t *read_cache_status,
                          uint32_t *physical_disk_cache, lsm_flag flags)
{
    if (LSM_FLAG_UNUSED_CHECK(flags)) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    int rc = LSM_ERR_OK;
    CONN_SETUP(c);

    if ((!LSM_IS_VOL(volume)) ||
        (write_cache_policy == NULL) || (write_cache_status == NULL) ||
        (read_cache_policy == NULL) || (read_cache_status == NULL) ||
        (physical_disk_cache == NULL))
        return LSM_ERR_INVALID_ARGUMENT;

    try {

        Value parameters = _create_volume_flag_param(volume, flags);
        Value response;

        rc = rpc(c, "volume_cache_info", parameters, response);
        if (LSM_ERR_OK == rc) {
            std::vector < Value > j = response.asArray();
            *write_cache_policy = j[0].asUint32_t();
            *write_cache_status = j[1].asUint32_t();
            *read_cache_policy = j[2].asUint32_t();
            *read_cache_status = j[3].asUint32_t();
            *physical_disk_cache = j[4].asUint32_t();
        }
    }
    catch(const ValueException & ve) {
        rc = log_exception(c, LSM_ERR_PLUGIN_BUG, "Unexpected type", ve.what());
    }

    if (rc != LSM_ERR_OK) {
        *write_cache_policy = LSM_VOLUME_WRITE_CACHE_POLICY_UNKNOWN;
        *write_cache_status = LSM_VOLUME_WRITE_CACHE_STATUS_UNKNOWN;
        *read_cache_policy = LSM_VOLUME_READ_CACHE_POLICY_UNKNOWN;
        *read_cache_status = LSM_VOLUME_READ_CACHE_STATUS_UNKNOWN;
        *physical_disk_cache = LSM_VOLUME_PHYSICAL_DISK_CACHE_UNKNOWN;
    }

    return rc;
}

int lsm_volume_physical_disk_cache_update(lsm_connect *c, lsm_volume *volume,
                                          uint32_t pdc, lsm_flag flags)
{
    CONN_SETUP(c);

    if (! LSM_IS_VOL(volume) || LSM_FLAG_UNUSED_CHECK(flags) ||
        ((pdc != LSM_VOLUME_PHYSICAL_DISK_CACHE_DISABLED) &&
         (pdc != LSM_VOLUME_PHYSICAL_DISK_CACHE_ENABLED))) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["volume"] = volume_to_value(volume);
    p["pdc"] = Value(pdc);
    p["flags"] = Value(flags);
    Value parameters(p);
    Value response;

    //No response data.
    return rpc(c, "volume_physical_disk_cache_update", parameters, response);
}

int lsm_volume_write_cache_policy_update(lsm_connect *c, lsm_volume *volume,
                                         uint32_t wcp, lsm_flag flags)
{
    CONN_SETUP(c);

    if (! LSM_IS_VOL(volume) || LSM_FLAG_UNUSED_CHECK(flags) ||
        ((wcp != LSM_VOLUME_WRITE_CACHE_POLICY_AUTO) &&
         (wcp != LSM_VOLUME_WRITE_CACHE_POLICY_WRITE_BACK) &&
         (wcp!= LSM_VOLUME_WRITE_CACHE_POLICY_WRITE_THROUGH))) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["volume"] = volume_to_value(volume);
    p["wcp"] = Value(wcp);
    p["flags"] = Value(flags);
    Value parameters(p);
    Value response;

    //No response data.
    return rpc(c, "volume_write_cache_policy_update", parameters, response);
}

int lsm_volume_read_cache_policy_update(lsm_connect *c, lsm_volume *volume,
                                        uint32_t rcp, lsm_flag flags)
{
    CONN_SETUP(c);

    if (! LSM_IS_VOL(volume) || LSM_FLAG_UNUSED_CHECK(flags) ||
        ((rcp != LSM_VOLUME_READ_CACHE_POLICY_DISABLED) &&
         (rcp != LSM_VOLUME_READ_CACHE_POLICY_ENABLED))) {
        return LSM_ERR_INVALID_ARGUMENT;
    }

    std::map < std::string, Value > p;
    p["volume"] = volume_to_value(volume);
    p["rcp"] = Value(rcp);
    p["flags"] = Value(flags);
    Value parameters(p);
    Value response;

    //No response data.
    return rpc(c, "volume_read_cache_policy_update", parameters, response);
}