Source code for hplefthandclient.client

# 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.
"""
HPLeftHand REST Client

.. module: HPLeftHandClient
.. moduleauthor: Kurt Martin

: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


[docs]class HPLeftHandClient: def __init__(self, api_url): self.api_url = api_url self.http = http.HTTPJSONRESTClient(self.api_url)
[docs] def debug_rest(self, flag): """ This is useful for debugging requests to LeftHand :param flag: set to True to enable debugging :type flag: bool """ self.http.set_debug_flag(flag)
[docs] def login(self, username, password): """ This authenticates against the LH OS REST server and creates a session. :param username: The username :type username: str :param password: The password :type password: str :returns: None """ self.http.authenticate(username, password)
[docs] def logout(self): """ This destroys the session and logs out from the LH OS server :returns: None """ self.http.unauthenticate()
[docs] def getClusters(self): """ Get the list of Clusters :returns: list of Clusters """ response, body = self.http.get('/clusters') return body
[docs] 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) return body
[docs] def getClusterByName(self, name): """ Get information about a cluster by name :param name: The name of the cluster to find :type name: str :returns: cluster :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_CLUSTER - cluster doesn't exist """ response, body = self.http.get('/clusters?name=%s' % name) return body
[docs] def getServers(self): """ Get the list of Servers :returns: list of Servers """ response, body = self.http.get('/servers') return body
[docs] 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) return body
[docs] def getServerByName(self, name): """ Get information about a server by name :param name: The name of the server to find :type name: str :returns: server :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_SERVER - server doesn't exist """ response, body = self.http.get('/servers?name=%s' % name) return body
[docs] def createServer(self, name, iqn, optional=None): """ Create a server by name :param name: The name of the server to create :type name: str :param iqn: The iSCSI qualified name :type name: str :param optional: Dictionary of optional params :type optional: dict .. code-block:: python 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 } :returns: server :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_SERVER - server doesn't exist """ info = {'name': name, 'iscsiIQN': iqn} if optional: info = self._mergeDict(info, optional) response, body = self.http.post('/servers', body=info) return body
[docs] def deleteServer(self, server_id): """ Delete a Server :param server_id: the server ID to delete :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_SERVER - The server does not exist """ response, body = self.http.delete('/servers/%s' % server_id) return body
[docs] def getSnapshots(self): """ Get the list of Snapshots :returns: list of Snapshots """ response, body = self.http.get('/snapshots') return body
[docs] def getSnapshot(self, snapshot_id): """ Get information about a Snapshot :returns: snapshot :raises: :class:`~hplefthandclient.exceptions.HTTPServerError` """ response, body = self.http.get('/snapshots/%s' % snapshot_id) return body
[docs] def getSnapshotByName(self, name): """ Get information about a snapshot by name :param name: The name of the snapshot to find :returns: volume :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_SNAP - shapshot doesn't exist """ response, body = self.http.get('/snapshots?name=%s' % name) return body
[docs] def createSnapshot(self, name, source_volume_id, optional=None): """ Create a snapshot of an existing Volume :param name: Name of the Snapshot :type name: str :param source_volume_id: The volume you want to snapshot :type source_volume_id: int :param optional: Dictionary of optional params :type optional: dict .. code-block:: python optional = { 'description' : "some comment", 'inheritAccess' : false } """ parameters = {'name': name} if optional: parameters = self._mergeDict(parameters, optional) info = {'action': 'createSnapshot', 'parameters': parameters} response, body = self.http.post('/volumes/%s' % source_volume_id, body=info) return body
[docs] def deleteSnapshot(self, snapshot_id): """ Delete a Snapshot :param snapshot_id: the snapshot ID to delete :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_SNAPSHOT - The snapshot does not exist """ response, body = self.http.delete('/snapshots/%s' % snapshot_id) return body
[docs] def cloneSnapshot(self, name, source_snapshot_id, optional=None): """ Create a clone of an existing Shapshot :param name: Name of the Snapshot clone :type name: str :param source_snapshot_id: The snapshot you want to clone :type source_snapshot_id: int :param optional: Dictionary of optional params :type optional: dict .. code-block:: python optional = { 'description' : "some comment" } """ parameters = {'name': name} if optional: parameters = self._mergeDict(parameters, optional) info = {'action': 'createSmartClone', 'parameters': parameters} response, body = self.http.post('/snapshots/%s' % source_snapshot_id, body=info) return body
[docs] def getVolumes(self): """ Get the list of Volumes :returns: list of Volumes """ response, body = self.http.get('/volumes') return body
[docs] def getVolume(self, volume_id): """ Get information about a volume :param volume_id: The id of the volume to find :type volume_id: str :returns: volume :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_VOL - volume doesn't exist """ response, body = self.http.get('/volumes/%s' % volume_id) return body
[docs] def getVolumeByName(self, name): """ Get information about a volume by name :param name: The name of the volume to find :type volume_id: str :returns: volume :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_VOL - volume doesn't exist """ response, body = self.http.get('/volumes?name=%s' % name) return body
[docs] def createVolume(self, name, cluster_id, size, optional=None): """ Create a new volume :param name: the name of the volume :type name: str :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 :type optional: dict .. code-block:: python optional = { '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} if optional: info = self._mergeDict(info, optional) response, body = self.http.post('/volumes', body=info) return body
[docs] def deleteVolume(self, volume_id): """ Delete a volume :param name: the name of the volume :type name: str :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_VOL - The volume does not exist """ response, body = self.http.delete('/volumes/%s' % volume_id) return body
[docs] def modifyVolume(self, volume_id, optional): """Modify an existing volume. :param volume_id: The id of the volume to find :type volume_id: str :returns: volume :raises: :class:`~hplefthandclient.exceptions.HTTPNotFound` - NON_EXISTENT_VOL - volume doesn't exist """ info = {'volume_id': volume_id} info = self._mergeDict(info, optional) response, body = self.http.put('/volumes/%s' % volume_id, body=info) return body
[docs] def cloneVolume(self, name, source_volume_id, optional=None): """ Create a clone of an existing Volume :param name: Name of the Volume clone :type name: str :param source_volume_id: The Volume you want to clone :type source_volume_id: int :param optional: Dictionary of optional params :type optional: dict .. code-block:: python optional = { 'description' : "some comment" } """ parameters = {'name': name} if optional: parameters = self._mergeDict(parameters, optional) info = {'action': 'createSmartClone', 'parameters': parameters} response, body = self.http.post('/volumes/%s' % source_volume_id, body=info) return body
[docs] 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 :type source_volume_id: int :param optional: Dictionary of optional params :type optional: dict .. code-block:: python optional = { 'Transport' : 0, 'Lun' : 1, } """ parameters = {'serverID': server_id, 'exclusiveAccess': True, 'readAccess': True, 'writeAccess': True} if optional: parameters = self._mergeDict(parameters, optional) info = {'action': 'addServerAccess', 'parameters': parameters} response, body = self.http.post('/volumes/%s' % volume_id, body=info) return body
[docs] def removeServerAccess(self, volume_id, server_id): """ Unassign a Volume from a Server :param volume_id: Volume ID of the volume :type name: int :param server_id: Server ID of the server to remove the volume fom :type source_volume_id: int """ parameters = {'serverID': server_id} info = {'action': 'removeServerAccess', 'parameters': parameters} response, body = self.http.post('/volumes/%s' % volume_id, body=info) return body
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