Hyperlink target "lefthand/storevirtual storage arrays" is not referenced.

This documentation is generated using the `Sphinx
<http://sphinx.pocoo.org/>`_ documentation generator. The source files
for the documentation are located in the *doc/* directory of the
**HPLeftHandClient** distribution. To generate the docs locally run the
following command from the root directory of the **HPLeftHandClient**

 documentation generator. The source files
for the documentation are located in the 

 distribution. To generate the docs locally run the
following command from the root directory of the 

**HPLeftHandClient** is a Python package containing a class that uses
HTTP REST calls to talk with an HP LeftHand/StoreVirtual drive array.
distribution containing tools for working with
`LeftHand/StoreVirtual Storage Arrays <http://www.hp.com/go/storevirtual>`_.
. This documentation attempts to explain
everything you need to know to use **HPLeftHandClient**.

 is a Python package containing a class that uses

`LeftHand/StoreVirtual Storage Arrays <http://www.hp.com/go/storevirtual>`_

.
everything you need to know to use 

:doc:`installation`
Instructions on how to get the distribution.

:doc:`api/index`
The complete API documentation, organized by module.

All issues should be reported (and can be tracked / voted for /
commented on) at the main `github issues
<https://github.com/WaltHP/python-hplefthandclient/issues>`_, in the "LeftHand Python Driver"
project.

commented on) at the main 

`github issues
<https://github.com/WaltHP/python-hplefthandclient/issues>`_

See the :doc:`changelog` for a full list of changes to HPLeftHandClient.

# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Hewlett Packard Development Company, L.P.
# All Rights Reserved.
#
#    Licensed 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.
""" 
Exceptions for the client

.. module: Exceptions

:Author: Walter A. Boring IV
:Description: This contains the HTTP exceptions that can come back from the REST calls
"""

class UnsupportedVersion(Exception):
    """
    Indicates that the user is trying to use an unsupported version of the API
    """
    pass

class CommandError(Exception):
    pass

class AuthorizationFailure(Exception):
    pass

class NoUniqueMatch(Exception):
    pass

class ClientException(Exception):
    """
    The base exception class for all exceptions this library raises.

    :param error: The error array 
    :type error: array

    """
    _error_code = None
    _error_desc = None

    _debug1 = None
    _debug2 = None

    def __init__(self, error=None):
        if error:
            if 'messageID' in error:
                self._error_code = error['messageID']
            if 'message' in error:
                self._error_desc = error['message']

            if 'debug1' in error:
                self._debug1 = error['debug1']
            if 'debug2' in error:
                self._debug2 = error['debug2']

    def get_code(self):
        return self._error_code

    def get_description(self):
        return self._error_desc

    def __str__(self):
        formatted_string = "%s (HTTP %s)" % (self.message, self.http_status)
        if self._error_code:
            formatted_string += " %s" % self._error_code
        if self._error_desc:
            formatted_string += " - %s" % self._error_desc

        if self._debug1:
            formatted_string += " (1: '%s')" % self._debug1

        if self._debug2:
            formatted_string += " (2: '%s')" % self._debug2
          
        return formatted_string

##
## 400 Errors
##

class HTTPBadRequest(ClientException):
    """
    HTTP 400 - Bad request: you sent some malformed data.
    """
    http_status = 400
    message = "Bad request"

class HTTPUnauthorized(ClientException):
    """
    HTTP 401 - Unauthorized: bad credentials.
    """
    http_status = 401
    message = "Unauthorized"

class HTTPForbidden(ClientException):
    """
    HTTP 403 - Forbidden: your credentials don't give you access to this
    resource.
    """
    http_status = 403
    message = "Forbidden"

class HTTPNotFound(ClientException):
    """
    HTTP 404 - Not found
    """
    http_status = 404
    message = "Not found"

class HTTPMethodNotAllowed(ClientException):
    """
    HTTP 405 - Method not Allowed 
    """
    http_status = 405
    message = "Method Not Allowed"

class HTTPNotAcceptable(ClientException):
    """
    HTTP 406 - Method not Acceptable
    """
    http_status = 406
    message = "Method Not Acceptable"

class HTTPProxyAuthRequired(ClientException):
    """
    HTTP 407 - The client must first authenticate itself with the proxy.
    """
    http_status = 407
    message = "Proxy Authentication Required"

class HTTPRequestTimeout(ClientException):
    """
    HTTP 408 - The server timed out waiting for the request.
    """
    http_status = 408
    message = "Request Timeout"

class HTTPConflict(ClientException):
    """
    HTTP 409 - Conflict: A Conflict happened on the server
    """
    http_status = 409
    message = "Conflict"

class HTTPGone(ClientException):
    """
    HTTP 410 - Indicates that the resource requested is no longer available and will not be available again.
    """
    http_status = 410
    message = "Gone"

class HTTPLengthRequired(ClientException):
    """
    HTTP 411 - The request did not specify the length of its content, which is required by the requested resource.
    """
    http_status = 411
    message = "Length Required"

class HTTPPreconditionFailed(ClientException):
    """
    HTTP 412 - The server does not meet one of the preconditions that the requester put on the request.
    """
    http_status = 412
    message = "Over limit"

class HTTPRequestEntityTooLarge(ClientException):
    """
    HTTP 413 - The request is larger than the server is willing or able to process
    """
    http_status = 413
    message = "Request Entity Too Large"

class HTTPRequestURITooLong(ClientException):
    """
    HTTP 414 - The URI provided was too long for the server to process.
    """
    http_status = 414
    message = "Request URI Too Large"

class HTTPUnsupportedMediaType(ClientException):
    """
    HTTP 415 - The request entity has a media type which the server or resource does not support.
    """
    http_status = 415
    message = "Unsupported Media Type"

class HTTPRequestedRangeNotSatisfiable(ClientException):
    """
    HTTP 416 - The client has asked for a portion of the file, but the server cannot supply that portion.
    """
    http_status = 416
    message = "Requested Range Not Satisfiable"

class HTTPExpectationFailed(ClientException):
    """
    HTTP 417 - The server cannot meet the requirements of the Expect request-header field.
    """
    http_status = 417
    message = "Expectation Failed"

class HTTPTeaPot(ClientException):
    """
    HTTP 418 - I'm a Tea Pot
    """
    http_status = 418
    message = "I'm A Teapot. (RFC 2324)"

##
## 500 Errors
##

class HTTPServerError(ClientException):
    """
    HTTP 500 - 
    """
    http_status = 500
    message = "Error"

# NotImplemented is a python keyword.
class HTTPNotImplemented(ClientException):
    """
    HTTP 501 - Not Implemented: the server does not support this operation.
    """
    http_status = 501
    message = "Not Implemented"

class HTTPBadGateway(ClientException):
    """
    HTTP 502 - The server was acting as a gateway or proxy and received an invalid response from the upstream server. 
    """
    http_status = 502
    message = "Bad Gateway"

class HTTPServiceUnavailable(ClientException):
    """
    HTTP 503 - The server is currently unavailable
    """
    http_status = 503
    message = "Service Unavailable"

class HTTPGatewayTimeout(ClientException):
    """
    HTTP 504 - The server was acting as a gateway or proxy and did not receive a timely response from the upstream server.
    """
    http_status = 504
    message = "Gateway Timeout"

class HTTPVersionNotSupported(ClientException):
    """
    HTTP 505 - The server does not support the HTTP protocol version used in the request.
    """
    http_status = 505
    message = "Version Not Supported"

# In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__()
# so we can do this:
#     _code_map = dict((c.http_status, c)
#                      for c in ClientException.__subclasses__())
#
# Instead, we have to hardcode it:
_code_map = dict((c.http_status, c) for c in [HTTPBadRequest, HTTPUnauthorized,
                   HTTPForbidden, HTTPNotFound, HTTPMethodNotAllowed,
                   HTTPNotAcceptable, HTTPProxyAuthRequired, HTTPRequestTimeout, 
                   HTTPConflict, HTTPGone, HTTPLengthRequired,
                   HTTPPreconditionFailed, HTTPRequestEntityTooLarge,
                   HTTPRequestURITooLong, HTTPUnsupportedMediaType,
                   HTTPRequestedRangeNotSatisfiable, HTTPExpectationFailed,
                   HTTPTeaPot, HTTPServerError,
                   HTTPNotImplemented, HTTPBadGateway,
                   HTTPServiceUnavailable, HTTPGatewayTimeout,
                   HTTPVersionNotSupported])

def from_response(response, body):
    """
    Return an instance of an ClientException or subclass
    based on an httplib2 response.

    Usage:: 

        resp, body = http.request(...)
        if resp.status != 200:
            raise exception_from_response(resp, body)

    """
    cls = _code_map.get(response.status, ClientException)
    return cls(body)

#
# Copyright 2013 Hewlett Packard Development Company, L.P.
#
#
#
"""
HP LeftHand REST Client

:Copyright: Copyright 2013, Hewlett Packard Development Company, L.P.
:License: Apache v2.0
 
"""

version_tuple = (1, 0, 0)

def get_version_string():
    if isinstance(version_tuple[-1], basestring):
        return '.'.join(map(str, version_tuple[:-1])) + version_tuple[-1]
    return '.'.join(map(str, version_tuple))

version = get_version_string()
"""Current version of HPLeftHandClient."""

#
#
#
#

"""
HPLeftHand HTTP Client
:Description: This is the HTTP Client that is used to make the actual calls.
 It includes the authentication that knows the cookie name for LH.

"""

import logging
import httplib2
import time
import pprint

try:
    import json
except ImportError:
    import simplejson as json

from hplefthandclient import exceptions

class HTTPJSONRESTClient(httplib2.Http):
    """
    An HTTP REST Client that sends and recieves JSON data as the body of the
    HTTP request.

    :param api_url: The url to the LH OS REST service
                    ie. https://<hostname or IP>:<port>/lhos
    :type api_url: str
    :param insecure: Use https? requires a local certificate
    :type insecure: bool

    """

    USER_AGENT = 'python-hplefthandclient'
    SESSION_COOKIE_NAME = 'Authorization'
    #API_VERSION = 'X-API-Version'
    #CHRP_VERSION = 'X_HP-CHRP-Client-Version'

    def __init__(self, api_url, insecure=False, http_log_debug=False):
        super(HTTPJSONRESTClient,
              self).__init__(disable_ssl_certificate_validation=True)
        self.session_key = None

        #should be http://<Server:Port>/lhos
        self.set_url(api_url)
        self.set_debug_flag(http_log_debug)

        self.times = []  # [("item", starttime, endtime), ...]

        # httplib2 overrides
        self.force_exception_to_status_code = True
        #self.disable_ssl_certificate_validation = insecure

        self._logger = logging.getLogger(__name__)

    def set_url(self, api_url):
        self.api_url = api_url.rstrip('/')
        self.api_url = self.api_url

    def set_debug_flag(self, flag):
        """
        This turns on/off http request/response debugging output to console

        :param flag: Set to True to enable debugging output
        :type flag: bool

        """
        self.http_log_debug = flag
        if self.http_log_debug:
            ch = logging.StreamHandler()
            self._logger.setLevel(logging.DEBUG)
            self._logger.addHandler(ch)

    def authenticate(self, user, password, optional=None):
        """
        This tries to create an authenticated session with the LH OS server

        :param user: The username
        :type user: str
        :param password: The password
        :type password: str

        """
        #this prevens re-auth attempt if auth fails
        self.auth_try = 1

        info = {'user': user, 'password': password}
        self._auth_optional = None

        if optional:
            self._auth_optional = optional
            info.update(optional)

        resp, body = self.post('/credentials', body=info)
        if body and 'authToken' in body:
            self.session_key = body['authToken']
        self.auth_try = 0
        self.user = user
        self.password = password

    def _reauth(self):
        self.authenticate(self.user, self.password, self._auth_optional)

    def unauthenticate(self):
        """
        This clears the authenticated session with the LH server.  It logs out.

        """
        #delete the session on the LH
        self.delete('/credentials/%s' % self.session_key)

    def get_timings(self):
        """
        Ths gives an array of the request timings since last reset_timings call
        """
        return self.times

    def reset_timings(self):
        """
        This resets the request/response timings array
        """
        self.times = []

    def _http_log_req(self, args, kwargs):
        if not self.http_log_debug:
            return

        string_parts = ['curl -i']
        for element in args:
            if element in ('GET', 'POST'):
                string_parts.append(' -X %s' % element)
            else:
                string_parts.append(' %s' % element)

        for element in kwargs['headers']:
            header = ' -H "%s: %s"' % (element, kwargs['headers'][element])
            string_parts.append(header)

        self._logger.debug("\nREQ: %s\n" % "".join(string_parts))
        if 'body' in kwargs:
            self._logger.debug("REQ BODY: %s\n" % (kwargs['body']))

    def _http_log_resp(self, resp, body):
            return
        self._logger.debug("RESP:%s\n", pprint.pformat(resp))
        self._logger.debug("RESP BODY:%s\n", body)

    def request(self, *args, **kwargs):
        """
        This makes an HTTP Request to the LH server.  You should use get, post,
        delete instead.
        """
        if self.session_key and self.auth_try != 1:
            kwargs.setdefault('headers',
                              {})[self.SESSION_COOKIE_NAME] = self.session_key

        kwargs.setdefault('headers', kwargs.get('headers', {}))
        kwargs['headers']['User-Agent'] = self.USER_AGENT
        kwargs['headers']['Accept'] = 'application/json'
            kwargs['headers']['Content-Type'] = 'application/json'
            kwargs['body'] = json.dumps(kwargs['body'])

        self._http_log_req(args, kwargs)
        resp, body = super(HTTPJSONRESTClient, self).request(*args, **kwargs)
        self._http_log_resp(resp, body)

        # Try and conver the body response to an object
        # This assumes the body of the reply is JSON
        if body:
            try:
                body = json.loads(body)
            except ValueError:
                #pprint.pprint("failed to decode json\n")
                pass
        else:
            body = None

        if resp.status >= 400:
            raise exceptions.from_response(resp, body)

        return resp, body

    def _time_request(self, url, method, **kwargs):
        start_time = time.time()
        resp, body = self.request(url, method, **kwargs)
        self.times.append(("%s %s" % (method, url),
                           start_time, time.time()))

    def _do_reauth(self, url, method, ex, **kwargs):
        print "_do_reauth called"
        try:
            if self.auth_try != 1:
                self._reauth()
                resp, body = self._time_request(self.api_url + url,
                                                method, **kwargs)
                return resp, body
            else:
                raise ex
        except exceptions.HTTPUnauthorized:
            raise ex

    def _cs_request(self, url, method, **kwargs):
        # Perform the request once. If we get a 401 back then it
        # might be because the auth token expired, so try to
        # re-authenticate and try again. If it still fails, bail.
        try:
            resp, body = self._time_request(self.api_url + url, method,
                                            **kwargs)
            return resp, body
        except exceptions.HTTPUnauthorized, ex:
            print "_CS_REQUEST HTTPUnauthorized"
            resp, body = self._do_reauth(url, method, ex, **kwargs)
        except exceptions.HTTPForbidden, ex:
            print "_CS_REQUEST HTTPForbidden"

    def get(self, url, **kwargs):
        """
        Make an HTTP GET request to the server.

        .. code-block:: python

            #example call
            try {
                headers, body = http.get('/volumes')
            } except exceptions.HTTPUnauthorized as ex:
                print "Not logged in"
            }

        :param url: The relative url from the LH api_url
        :type url: str

        :returns: headers - dict of HTTP Response headers
        :returns: body - the body of the response.  If the body was JSON,
        it will be an object
        """
        return self._cs_request(url, 'GET', **kwargs)

    def post(self, url, **kwargs):
        """
        Make an HTTP POST request to the server.

            try {
                info = {'name': 'new volume name', 'sizeMiB': 300}
                headers, body = http.post('/volumes', body=info)
            }

        """
        return self._cs_request(url, 'POST', **kwargs)

    def put(self, url, **kwargs):
        """
        Make an HTTP PUT request to the server.

            try {
                info = {'name': 'something'}
                headers, body = http.put('/volumes', body=info)
            }

        """
        return self._cs_request(url, 'PUT', **kwargs)

    def delete(self, url, **kwargs):
        """
        Make an HTTP DELETE request to the server.

            try {
                headers, body = http.delete('/volumes/%s' % name)
            }

        """
        return self._cs_request(url, 'DELETE', **kwargs)

#
#
#
#
"""
HPLeftHand REST Client

.. module: HPLeftHandClient
.. moduleauthor: Kurt Martin
.. moduleauthor: Walter A. Boring IV

:Author: Kurt Martin
:Description: This is the LeftHand/StoreVirtual Client that talks to the
LeftHand OS REST Service.

This client requires and works with version 11.5 of the LeftHand firmware

"""

from hplefthandclient import http

class HPLeftHandClient:

    def __init__(self, api_url):
        self.api_url = api_url
        self.http = http.HTTPJSONRESTClient(self.api_url)

    def debug_rest(self, flag):
        """
        This is useful for debugging requests to 3PAR

        :param flag: set to True to enable debugging

        """
        self.http.set_debug_flag(flag)

    def login(self, username, password):
        """
        This authenticates against the LH OS REST server and creates a session.

        :param username: The username
        :type username: str

        :returns: None

        """
        self.http.authenticate(username, password)

    def logout(self):
        """
        This destroys the session and logs out from the LH OS server

        """
        self.http.unauthenticate()

    def getClusters(self):
        """
        Get the list of Clusters

        :returns: list of Clusters
        """
        response, body = self.http.get('/clusters')
        return body

    def getCluster(self, cluster_id):
        """
        Get information about a Cluster

        :param cluster_id: The id of the cluster to find
        :type cluster_id: str

        :returns: cluster
        """
        response, body = self.http.get('/clusters/%s' % cluster_id)

    def getClusterByName(self, name):
        """
        Get information about a cluster by name

        :param name: The name of the cluster to find
        :type name: str

        :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` -
        NON_EXISTENT_CLUSTER - cluster doesn't exist
        """
        response, body = self.http.get('/clusters?name=%s' % name)

    def getServers(self):
        """
        Get the list of Servers

        :returns: list of Servers
        """
        response, body = self.http.get('/servers')

    def getServer(self, server_id):
        """
        Get information about a server

        :param server_id: The id of the server to find
        :type server_id: str

        :returns: server
        :raises: :class:`~hplefthandclient.exceptions.HTTPServerError`
        """
        response, body = self.http.get('/servers/%s' % server_id)

    def getServerByName(self, name):
        """
        Get information about a server by name

        :param name: The name of the server to find

        NON_EXISTENT_SERVER - server doesn't exist
        """
        response, body = self.http.get('/servers?name=%s' % name)

    def createServer(self, name, iqn, optional=None):
        """
        Create a server by name

        :param name: The name of the server to create
        :param iqn: The iSCSI qualified name
        :param optional: Dictionary of optional params
        :type optional: dict

            optional = {
                'description' : "some comment",
                'iscsiEnabled' : True,
                'chapName': "some chap name",
                'chapAuthenticationRequired': False,
                'chapInitiatorSecret': "initiator secret",
                'chapTargetSecret': "target secret",
                'iscsiLoadBalancingEnabled': True,
                'controllingServerName': "server name",
                'fibreChannelEnabled': False,
                'inServerCluster": True
            }

        """
        info = {'name': name, 'iscsiIQN': iqn}
            info = self._mergeDict(info, optional)

        response, body = self.http.post('/servers', body=info)

    def deleteServer(self, server_id):
        """
        Delete a Server

        :param server_id: the server ID to delete

        NON_EXISTENT_SERVER - The server does not exist
        """
        response, body = self.http.delete('/servers/%s' % server_id)

    def getSnapshots(self):
        """
        Get the list of Snapshots

        :returns: list of Snapshots
        """
        response, body = self.http.get('/snapshots')

    def getSnapshot(self, snapshot_id):
        """
        Get information about a Snapshot

        :returns: snapshot
        """
        response, body = self.http.get('/snapshots/%s' % snapshot_id)

    def getSnapshotByName(self, name):
        """
        Get information about a snapshot by name

        :param name: The name of the snapshot to find

        :returns: volume
        NON_EXISTENT_SNAP - shapshot doesn't exist
        """
        response, body = self.http.get('/snapshots?name=%s' % name)

    def createSnapshot(self, name, source_volume_id, optional=None):
        """
        Create a snapshot of an existing Volume

        :param name: Name of the Snapshot
        :param source_volume_id: The volume you want to snapshot
        :type source_volume_id: int

                'inheritAccess' : false
            }

        """
        parameters = {'name': name}
            parameters = self._mergeDict(parameters, optional)

        info = {'action': 'createSnapshot',
                'parameters': parameters}

        response, body = self.http.post('/volumes/%s' % source_volume_id,
                                        body=info)

    def deleteSnapshot(self, snapshot_id):
        """
        Delete a Snapshot

        :param snapshot_id: the snapshot ID to delete

        NON_EXISTENT_SNAPSHOT - The snapshot does not exist
        """
        response, body = self.http.delete('/snapshots/%s' % snapshot_id)

    def cloneSnapshot(self, name, source_snapshot_id, optional=None):
        """
        Create a clone of an existing Shapshot

        :param name: Name of the Snapshot clone
        :param source_snapshot_id: The snapshot you want to clone
        :type source_snapshot_id: int

                'description' : "some comment"
            }

        """

        info = {'action': 'createSmartClone',

        response, body = self.http.post('/snapshots/%s' % source_snapshot_id,

    def getVolumes(self):
        """
        Get the list of Volumes

        :returns: list of Volumes
        """
        response, body = self.http.get('/volumes')

    def getVolume(self, volume_id):
        """
        Get information about a volume

        :param volume_id: The id of the volume to find
        :type volume_id: str

        NON_EXISTENT_VOL - volume doesn't exist
        """
        response, body = self.http.get('/volumes/%s' % volume_id)

    def getVolumeByName(self, name):
        """
        Get information about a volume by name

        :param name: The name of the volume to find

        """
        response, body = self.http.get('/volumes?name=%s' % name)

    def createVolume(self, name, cluster_id, size, optional=None):
        """ Create a new volume

        :param name: the name of the volume
        :param cluster_id: the cluster Id
        :type cluster_id: int
        :param sizeKB: size in KB for the volume
        :type sizeKB: int
        :param optional: dict of other optional items

             'description': 'some comment',
             'isThinProvisioned': 'true',
             'autogrowSeconds': 200,
             'clusterName': 'somename',
             'isAdaptiveOptimizationEnabled': 'true',
             'dataProtectionLevel': 2,
            }

        :returns: List of Volumes

        :raises: :class:`~hplefthandclient.exceptions.HTTPConflict` -
        EXISTENT_SV - Volume Exists already
        """
        info = {'name': name, 'clusterID': cluster_id, 'size': size}

        response, body = self.http.post('/volumes', body=info)

    def deleteVolume(self, volume_id):
        """
        Delete a volume

        NON_EXISTENT_VOL - The volume does not exist
        """
        response, body = self.http.delete('/volumes/%s' % volume_id)

    def modifyVolume(self, volume_id, optional):
        """Modify an existing volume.

        """
        info = {'volume_id': volume_id}
        info = self._mergeDict(info, optional)
        response, body = self.http.put('/volumes/%s' % volume_id, body=info)

    def cloneVolume(self, name, source_volume_id, optional=None):
        """
        Create a clone of an existing Volume

        :param name: Name of the Volume clone
        :param source_volume_id: The Volume you want to clone

            }

        """

    def addServerAccess(self, volume_id, server_id, optional=None):
        """
        Assign a Volume to a Server

        :param volume_id: Volume ID of the volume
        :type name: int
        :param server_id: Server ID of the server to add the volume to

                'Transport' : 0,
                'Lun' : 1,
            }

        """
        parameters = {'serverID': server_id,
                      'exclusiveAccess': True,
                      'readAccess': True,
                      'writeAccess': True}

        info = {'action': 'addServerAccess',

        response, body = self.http.post('/volumes/%s' % volume_id,

    def removeServerAccess(self, volume_id, server_id):
        """
        Unassign a Volume from a Server

        :param server_id: Server ID of the server to remove the volume fom

        """
        parameters = {'serverID': server_id}

        info = {'action': 'removeServerAccess',

    def _mergeDict(self, dict1, dict2):
        """
        Safely merge 2 dictionaries together

        :param dict1: The first dictionary
        :type dict1: dict
        :param dict2: The second dictionary
        :type dict2: dict

        :returns: dict

        :raises Exception: dict1, dict2 is not a dictionary
        """
        if type(dict1) is not dict:
            raise Exception("dict1 is not a dictionary")
        if type(dict2) is not dict:
            raise Exception("dict2 is not a dictionary")

        dict3 = dict1.copy()
        dict3.update(dict2)
        return dict3

addServerAccess() (hplefthandclient.client.HPLeftHandClient method)

cloneSnapshot() (hplefthandclient.client.HPLeftHandClient method)

createServer() (hplefthandclient.client.HPLeftHandClient method)

createSnapshot() (hplefthandclient.client.HPLeftHandClient method)

createVolume() (hplefthandclient.client.HPLeftHandClient method)

deleteServer() (hplefthandclient.client.HPLeftHandClient method)

deleteSnapshot() (hplefthandclient.client.HPLeftHandClient method)

deleteVolume() (hplefthandclient.client.HPLeftHandClient method)

getClusterByName() (hplefthandclient.client.HPLeftHandClient method)

getServerByName() (hplefthandclient.client.HPLeftHandClient method)

getSnapshotByName() (hplefthandclient.client.HPLeftHandClient method)

getSnapshots() (hplefthandclient.client.HPLeftHandClient method)

getVolumeByName() (hplefthandclient.client.HPLeftHandClient method)

modifyVolume() (hplefthandclient.client.HPLeftHandClient method)

removeServerAccess() (hplefthandclient.client.HPLeftHandClient method)

get_description() (hplefthandclient.exceptions.ClientException method)

http_status (hplefthandclient.exceptions.HTTPBadGateway attribute)

http_status (hplefthandclient.exceptions.HTTPBadRequest attribute)

http_status (hplefthandclient.exceptions.HTTPConflict attribute)

http_status (hplefthandclient.exceptions.HTTPExpectationFailed attribute)

message (hplefthandclient.exceptions.HTTPExpectationFailed attribute)

http_status (hplefthandclient.exceptions.HTTPForbidden attribute)

http_status (hplefthandclient.exceptions.HTTPGatewayTimeout attribute)

message (hplefthandclient.exceptions.HTTPGatewayTimeout attribute)

http_status (hplefthandclient.exceptions.HTTPLengthRequired attribute)

message (hplefthandclient.exceptions.HTTPLengthRequired attribute)

http_status (hplefthandclient.exceptions.HTTPMethodNotAllowed attribute)

message (hplefthandclient.exceptions.HTTPMethodNotAllowed attribute)

http_status (hplefthandclient.exceptions.HTTPNotAcceptable attribute)

message (hplefthandclient.exceptions.HTTPNotAcceptable attribute)

http_status (hplefthandclient.exceptions.HTTPNotFound attribute)

http_status (hplefthandclient.exceptions.HTTPNotImplemented attribute)

message (hplefthandclient.exceptions.HTTPNotImplemented attribute)

http_status (hplefthandclient.exceptions.HTTPPreconditionFailed attribute)

message (hplefthandclient.exceptions.HTTPPreconditionFailed attribute)

http_status (hplefthandclient.exceptions.HTTPProxyAuthRequired attribute)

message (hplefthandclient.exceptions.HTTPProxyAuthRequired attribute)

http_status (hplefthandclient.exceptions.HTTPRequestEntityTooLarge attribute)

message (hplefthandclient.exceptions.HTTPRequestEntityTooLarge attribute)

http_status (hplefthandclient.exceptions.HTTPRequestTimeout attribute)

message (hplefthandclient.exceptions.HTTPRequestTimeout attribute)

http_status (hplefthandclient.exceptions.HTTPRequestURITooLong attribute)

message (hplefthandclient.exceptions.HTTPRequestURITooLong attribute)

message (hplefthandclient.exceptions.HTTPRequestedRangeNotSatisfiable attribute)

http_status (hplefthandclient.exceptions.HTTPServerError attribute)

http_status (hplefthandclient.exceptions.HTTPServiceUnavailable attribute)

message (hplefthandclient.exceptions.HTTPServiceUnavailable attribute)

http_status (hplefthandclient.exceptions.HTTPUnauthorized attribute)

message (hplefthandclient.exceptions.HTTPUnauthorized attribute)

http_status (hplefthandclient.exceptions.HTTPUnsupportedMediaType attribute)

message (hplefthandclient.exceptions.HTTPUnsupportedMediaType attribute)

http_status (hplefthandclient.exceptions.HTTPVersionNotSupported attribute)

message (hplefthandclient.exceptions.HTTPVersionNotSupported attribute)

SESSION_COOKIE_NAME (hplefthandclient.http.HTTPJSONRESTClient attribute)

authenticate() (hplefthandclient.http.HTTPJSONRESTClient method)

reset_timings() (hplefthandclient.http.HTTPJSONRESTClient method)

set_debug_flag() (hplefthandclient.http.HTTPJSONRESTClient method)

unauthenticate() (hplefthandclient.http.HTTPJSONRESTClient method)