¡@

Home 

OpenStack Study: hp_msa_client.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright 2014 Objectif Libre

#

# 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.

#

from hashlib import md5

import urllib2

from lxml import etree

**** CubicPower OpenStack Study ****

class HPMSAConnectionError(Exception):

pass

**** CubicPower OpenStack Study ****

class HPMSAAuthenticationError(Exception):

pass

**** CubicPower OpenStack Study ****

class HPMSARequestError(Exception):

pass

**** CubicPower OpenStack Study ****

class HPMSAClient(object):

**** CubicPower OpenStack Study ****

    def __init__(self, host, login, password, protocol='http'):

        self._login = login

        self._password = password

        self._base_url = "%s://%s/api" % (protocol, host)

        self._session_key = None

**** CubicPower OpenStack Study ****

    def _get_auth_token(self, xml):

        """Parse an XML authentication reply to extract the session key."""

        self._session_key = None

        obj = etree.XML(xml).find("OBJECT")

        for prop in obj.iter("PROPERTY"):

            if prop.get("name") == "response":

                self._session_key = prop.text

                break

**** CubicPower OpenStack Study ****

    def login(self):

        """Authenticates the service on the device."""

        hash = md5("%s_%s" % (self._login, self._password))

        digest = hash.hexdigest()

        url = self._base_url + "/login/" + digest

        try:

            xml = urllib2.urlopen(url).read()

        except urllib2.URLError:

            raise HPMSAConnectionError()

        self._get_auth_token(xml)

        if self._session_key is None:

            raise HPMSAAuthenticationError()

**** CubicPower OpenStack Study ****

    def _assert_response_ok(self, tree):

        """Parses the XML returned by the device to check the return code.

        Raises a HPMSARequestError error if the return code is not 0.

        """

        for obj in tree.iter():

            if obj.get("basetype") != "status":

                continue

            ret_code = ret_str = None

            for prop in obj.iter("PROPERTY"):

                if prop.get("name") == "return-code":

                    ret_code = prop.text

                elif prop.get("name") == "response":

                    ret_str = prop.text

            if ret_code != "0":

                raise HPMSARequestError(ret_str)

            else:

                return

        raise HPMSARequestError("No status found")

**** CubicPower OpenStack Study ****

    def _build_request_url(self, path, args=None, **kargs):

        url = self._base_url + path

        if kargs:

            url += '/' + '/'.join(["%s/%s" % (k.replace('_', '-'), v)

                                   for (k, v) in kargs.items()])

        if args:

            if not isinstance(args, list):

                args = [args]

            url += '/' + '/'.join(args)

        return url

**** CubicPower OpenStack Study ****

    def _request(self, path, args=None, **kargs):

        """Performs an HTTP request on the device.

        Raises a HPMSARequestError if the device returned but the status is

        not 0. The device error message will be used in the exception message.

        If the status is OK, returns the XML data for further processing.

        """

        url = self._build_request_url(path, args, **kargs)

        headers = {'dataType': 'api', 'sessionKey': self._session_key}

        req = urllib2.Request(url, headers=headers)

        try:

            xml = urllib2.urlopen(req).read()

        except urllib2.URLError:

            raise HPMSAConnectionError()

        try:

            tree = etree.XML(xml)

        except etree.LxmlError:

            raise HPMSAConnectionError()

        self._assert_response_ok(tree)

        return tree

**** CubicPower OpenStack Study ****

    def logout(self):

        url = self._base_url + '/exit'

        try:

            urllib2.urlopen(url)

            return True

        except HPMSARequestError:

            return False

**** CubicPower OpenStack Study ****

    def create_volume(self, vdisk, name, size):

        # NOTE: size is in this format: [0-9]+GB

        self._request("/create/volume", name, vdisk=vdisk, size=size)

        return None

**** CubicPower OpenStack Study ****

    def delete_volume(self, name):

        self._request("/delete/volumes", name)

**** CubicPower OpenStack Study ****

    def extend_volume(self, name, added_size):

        self._request("/expand/volume", name, size=added_size)

**** CubicPower OpenStack Study ****

    def create_snapshot(self, volume_name, snap_name):

        self._request("/create/snapshots", snap_name, volumes=volume_name)

**** CubicPower OpenStack Study ****

    def delete_snapshot(self, snap_name):

        self._request("/delete/snapshot", ["cleanup", snap_name])

**** CubicPower OpenStack Study ****

    def vdisk_exists(self, vdisk):

        try:

            self._request("/show/vdisks", vdisk)

            return True

        except HPMSARequestError:

            return False

**** CubicPower OpenStack Study ****

    def vdisk_stats(self, vdisk):

        stats = {'free_capacity_gb': 0,

                 'total_capacity_gb': 0}

        tree = self._request("/show/vdisks", vdisk)

        for obj in tree.iter():

            if obj.get("basetype") != "virtual-disks":

                continue

            for prop in obj.iter("PROPERTY"):

                # the sizes are given in number of blocks of 512 octets

                if prop.get("name") == "size-numeric":

                    stats['total_capacity_gb'] = \

                        int(prop.text) * 512 / (10 ** 9)

                elif prop.get("name") == "freespace-numeric":

                    stats['free_capacity_gb'] = \

                        int(prop.text) * 512 / (10 ** 9)

        return stats

**** CubicPower OpenStack Study ****

    def _get_first_available_lun_for_host(self, host):

        luns = []

        tree = self._request("/show/host-maps", host)

        for obj in tree.iter():

            if obj.get("basetype") != "host-view-mappings":

                continue

            for prop in obj.iter("PROPERTY"):

                if prop.get("name") == "lun":

                    luns.append(int(prop.text))

        lun = 1

        while True:

            if lun not in luns:

                return lun

            lun += 1

**** CubicPower OpenStack Study ****

    def map_volume(self, volume_name, wwpns):

        # NOTE(gpocentek): we assume that luns will be the same for all hosts

        lun = self._get_first_available_lun_for_host(wwpns[0])

        hosts = ",".join(wwpns)

        self._request("/map/volume", volume_name,

                      lun=str(lun), host=hosts, access="rw")

        return lun

**** CubicPower OpenStack Study ****

    def unmap_volume(self, volume_name, wwpns):

        hosts = ",".join(wwpns)

        self._request("/unmap/volume", volume_name, host=hosts)

**** CubicPower OpenStack Study ****

    def get_active_target_ports(self):

        ports = []

        tree = self._request("/show/ports")

        for obj in tree.iter():

            if obj.get("basetype") != "port":

                continue

            port = {}

            for prop in obj.iter("PROPERTY"):

                prop_name = prop.get("name")

                if prop_name in ["port-type", "target-id", "status"]:

                    port[prop_name] = prop.text

            if port['status'] != 'Up':

                continue

            ports.append(port)

        return ports

**** CubicPower OpenStack Study ****

    def get_active_fc_target_ports(self):

        ports = []

        for port in self.get_active_target_ports():

            if port['port-type'] == "FC":

                ports.append(port['target-id'])

        return ports

**** CubicPower OpenStack Study ****

    def copy_volume(self, source_name, target_name, vdisk):

        self._request("/volumecopy", target_name,

                      dest_vdisk=vdisk,

                      source_volume=source_name,

                      prompt='yes')