components/openstack/cinder/files/zfssa/restclient.py
changeset 3998 5bd484384122
parent 3997 0ca3f3d6c919
child 4002 95b8f35fcdd5
--- a/components/openstack/cinder/files/zfssa/restclient.py	Fri Mar 20 03:13:26 2015 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,353 +0,0 @@
-# Copyright (c) 2014, Oracle and/or its affiliates. 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.
-"""
-ZFS Storage Appliance REST API Client Programmatic Interface
-"""
-
-import httplib
-import json
-import time
-import urllib2
-import StringIO
-
-from cinder.openstack.common import log
-
-LOG = log.getLogger(__name__)
-
-
-class Status:
-    """Result HTTP Status"""
-
-    def __init__(self):
-        pass
-
-    #: Request return OK
-    OK = httplib.OK
-
-    #: New resource created successfully
-    CREATED = httplib.CREATED
-
-    #: Command accepted
-    ACCEPTED = httplib.ACCEPTED
-
-    #: Command returned OK but no data will be returned
-    NO_CONTENT = httplib.NO_CONTENT
-
-    #: Bad Request
-    BAD_REQUEST = httplib.BAD_REQUEST
-
-    #: User is not authorized
-    UNAUTHORIZED = httplib.UNAUTHORIZED
-
-    #: The request is not allowed
-    FORBIDDEN = httplib.FORBIDDEN
-
-    #: The requested resource was not found
-    NOT_FOUND = httplib.NOT_FOUND
-
-    #: The request is not allowed
-    NOT_ALLOWED = httplib.METHOD_NOT_ALLOWED
-
-    #: Request timed out
-    TIMEOUT = httplib.REQUEST_TIMEOUT
-
-    #: Invalid request
-    CONFLICT = httplib.CONFLICT
-
-    #: Service Unavailable
-    BUSY = httplib.SERVICE_UNAVAILABLE
-
-
-class RestResult(object):
-    """Result from a REST API operation"""
-    def __init__(self, response=None, err=None):
-        """Initialize a RestResult containing the results from a REST call
-        :param response: HTTP response
-        """
-        self.response = response
-        self.error = err
-        self.data = ""
-        self.status = 0
-        if self.response is not None:
-            self.status = self.response.getcode()
-            result = self.response.read()
-            while result:
-                self.data += result
-                result = self.response.read()
-
-        if self.error is not None:
-            self.status = self.error.code
-            self.data = httplib.responses[self.status]
-
-        LOG.debug('response code: %s' % self.status)
-        LOG.debug('response data: %s' % self.data)
-
-    def get_header(self, name):
-        """Get an HTTP header with the given name from the results
-
-        :param name: HTTP header name
-        :return: The header value or None if no value is found
-        """
-        if self.response is None:
-            return None
-        info = self.response.info()
-        return info.getheader(name)
-
-
-class RestClientError(Exception):
-    """Exception for ZFS REST API client errors"""
-    def __init__(self, status, name="ERR_INTERNAL", message=None):
-
-        """Create a REST Response exception
-
-        :param status: HTTP response status
-        :param name: The name of the REST API error type
-        :param message: Descriptive error message returned from REST call
-        """
-        Exception.__init__(self, message)
-        self.code = status
-        self.name = name
-        self.msg = message
-        if status in httplib.responses:
-            self.msg = httplib.responses[status]
-
-    def __str__(self):
-        return "%d %s %s" % (self.code, self.name, self.msg)
-
-
-class RestClientURL(object):
-    """ZFSSA urllib2 client"""
-    def __init__(self, url, **kwargs):
-        """
-        Initialize a REST client.
-
-        :param url: The ZFSSA REST API URL
-        :key session: HTTP Cookie value of x-auth-session obtained from a
-                      normal BUI login.
-        :key timeout: Time in seconds to wait for command to complete.
-                      (Default is 60 seconds)
-        """
-        self.url = url
-        self.local = kwargs.get("local", False)
-        self.base_path = kwargs.get("base_path", "/api")
-        self.timeout = kwargs.get("timeout", 60)
-        self.headers = None
-        if kwargs.get('session'):
-            self.headers['x-auth-session'] = kwargs.get('session')
-
-        self.headers = {"content-type": "application/json"}
-        self.do_logout = False
-        self.auth_str = None
-
-    def _path(self, path, base_path=None):
-        """build rest url path"""
-        if path.startswith("http://") or path.startswith("https://"):
-            return path
-        if base_path is None:
-            base_path = self.base_path
-        if not path.startswith(base_path) and not (
-                self.local and ("/api" + path).startswith(base_path)):
-            path = "%s%s" % (base_path, path)
-        if self.local and path.startswith("/api"):
-            path = path[4:]
-        return self.url + path
-
-    def authorize(self):
-        """Performs authorization setting x-auth-session"""
-        self.headers['authorization'] = 'Basic %s' % self.auth_str
-        if 'x-auth-session' in self.headers:
-            del self.headers['x-auth-session']
-
-        try:
-            result = self.post("/access/v1")
-            del self.headers['authorization']
-            if result.status == httplib.CREATED:
-                self.headers['x-auth-session'] = \
-                    result.get_header('x-auth-session')
-                self.do_logout = True
-                LOG.info('ZFSSA version: %s' %
-                         result.get_header('x-zfssa-version'))
-
-            elif result.status == httplib.NOT_FOUND:
-                raise RestClientError(result.status, name="ERR_RESTError",
-                                      message="REST Not Available: \
-                                      Please Upgrade")
-
-        except RestClientError as err:
-            del self.headers['authorization']
-            raise err
-
-    def login(self, auth_str):
-        """
-        Login to an appliance using a user name and password and start
-        a session like what is done logging into the BUI.  This is not a
-        requirement to run REST commands, since the protocol is stateless.
-        What is does is set up a cookie session so that some server side
-        caching can be done.  If login is used remember to call logout when
-        finished.
-
-        :param auth_str: Authorization string (base64)
-        """
-        self.auth_str = auth_str
-        self.authorize()
-
-    def logout(self):
-        """Logout of an appliance"""
-        result = None
-        try:
-            result = self.delete("/access/v1", base_path="/api")
-        except RestClientError:
-            pass
-
-        self.headers.clear()
-        self.do_logout = False
-        return result
-
-    def islogin(self):
-        """return if client is login"""
-        return self.do_logout
-
-    @staticmethod
-    def mkpath(*args, **kwargs):
-        """Make a path?query string for making a REST request
-
-        :cmd_params args: The path part
-        :cmd_params kwargs: The query part
-        """
-        buf = StringIO()
-        query = "?"
-        for arg in args:
-            buf.write("/")
-            buf.write(arg)
-        for k in kwargs:
-            buf.write(query)
-            if query == "?":
-                query = "&"
-            buf.write(k)
-            buf.write("=")
-            buf.write(kwargs[k])
-        return buf.getvalue()
-
-    def request(self, path, request, body=None, **kwargs):
-        """Make an HTTP request and return the results
-
-        :param path: Path used with the initiazed URL to make a request
-        :param request: HTTP request type (GET, POST, PUT, DELETE)
-        :param body: HTTP body of request
-        :key accept: Set HTTP 'Accept' header with this value
-        :key base_path: Override the base_path for this request
-        :key content: Set HTTP 'Content-Type' header with this value
-        """
-        out_hdrs = dict.copy(self.headers)
-        if kwargs.get("accept"):
-            out_hdrs['accept'] = kwargs.get("accept")
-
-        if body is not None:
-            if isinstance(body, dict):
-                body = str(json.dumps(body))
-
-        if body and len(body):
-            out_hdrs['content-length'] = len(body)
-
-        zfssaurl = self._path(path, kwargs.get("base_path"))
-        req = urllib2.Request(zfssaurl, body, out_hdrs)
-        req.get_method = lambda: request
-        maxreqretries = kwargs.get("maxreqretries", 10)
-        retry = 0
-        response = None
-
-        LOG.debug('request: %s %s' % (request, zfssaurl))
-        LOG.debug('out headers: %s' % out_hdrs)
-        if body is not None and body != '':
-            LOG.debug('body: %s' % body)
-
-        while retry < maxreqretries:
-            try:
-                response = urllib2.urlopen(req, timeout=self.timeout)
-            except urllib2.HTTPError as err:
-                LOG.error('REST Not Available: %s' % err.code)
-                if err.code == httplib.SERVICE_UNAVAILABLE and \
-                   retry < maxreqretries:
-                    retry += 1
-                    time.sleep(1)
-                    LOG.error('Server Busy retry request: %s' % retry)
-                    continue
-                if (err.code == httplib.UNAUTHORIZED or
-                    err.code == httplib.INTERNAL_SERVER_ERROR) and \
-                   '/access/v1' not in zfssaurl:
-                    try:
-                        LOG.error('Authorizing request retry: %s, %s' %
-                                  (zfssaurl, retry))
-                        self.authorize()
-                        req.add_header('x-auth-session',
-                                       self.headers['x-auth-session'])
-                    except RestClientError:
-                        pass
-                    retry += 1
-                    time.sleep(1)
-                    continue
-
-                return RestResult(err=err)
-
-            except urllib2.URLError as err:
-                LOG.error('URLError: %s' % err.reason)
-                raise RestClientError(-1, name="ERR_URLError",
-                                      message=err.reason)
-
-            break
-
-        if response and response.getcode() == httplib.SERVICE_UNAVAILABLE and \
-           retry >= maxreqretries:
-            raise RestClientError(response.getcode(), name="ERR_HTTPError",
-                                  message="REST Not Available: Disabled")
-
-        return RestResult(response=response)
-
-    def get(self, path, **kwargs):
-        """
-        Make an HTTP GET request
-
-        :param path: Path to resource.
-        """
-        return self.request(path, "GET", **kwargs)
-
-    def post(self, path, body="", **kwargs):
-        """Make an HTTP POST request
-
-        :param path: Path to resource.
-        :param body: Post data content
-        """
-        return self.request(path, "POST", body, **kwargs)
-
-    def put(self, path, body="", **kwargs):
-        """Make an HTTP PUT request
-
-        :param path: Path to resource.
-        :param body: Put data content
-        """
-        return self.request(path, "PUT", body, **kwargs)
-
-    def delete(self, path, **kwargs):
-        """Make an HTTP DELETE request
-
-        :param path: Path to resource that will be deleted.
-        """
-        return self.request(path, "DELETE", **kwargs)
-
-    def head(self, path, **kwargs):
-        """Make an HTTP HEAD request
-
-        :param path: Path to resource.
-        """
-        return self.request(path, "HEAD", **kwargs)