Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
# Copyright 2014 TrilioData Inc.
# All Rights Reserved.
"""
An extension module for novaclient that allows the `nova` application access to
the contego API extensions.
"""

import os
import re
import json
import time
import requests
try:
    import urllib.parse as parse
except ImportError:
    import urlparse as parse

from novaclient import utils
from novaclient import base
from novaclient import exceptions
from novaclient.v2 import servers

# Add new client capabilities here. Each key is a capability name and its value
# is the list of API capabilities upon which it depends.

CAPABILITIES = {}

CAPS_HELP = {}


def __pre_parse_args__():
    pass


def __post_parse_args__(args):
    pass


def _find_server(cs, server):
    """ Returns a server by name or ID. """
    return utils.find_resource(cs.contego, server)


def inherit_args(inherit_from_fn):
    """Decorator to inherit all of the utils.arg decorated agruments from
    another function.
    """

    def do_inherit(fn):
        if hasattr(inherit_from_fn, "arguments"):
            fn.arguments = inherit_from_fn.arguments
        return fn

    return do_inherit


class ContegoServer(servers.Server):
    """
    A server object extended to provide contego capabilities
    """

    def vast_instance(self):
        # return self.manager.vast_snapshot()
        pass


class ContegoServerManager(servers.ServerManager):
    resource_class = ContegoServer

    def __init__(self, client, *args, **kwargs):
        servers.ServerManager.__init__(self, client, *args, **kwargs)

        # Make sure this instance is available as contego.
        if not (hasattr(client, "contego")):
            setattr(client, "contego", self)

    # Capabilities must be computed lazily because self.api.client isn't
    # available in __init__

    def setup_capabilities(self):
        api_caps = self.get_info()["capabilities"]
        self.capabilities = [
            cap
            for cap in CAPABILITIES.keys()
            if all([api_req in api_caps for api_req in CAPABILITIES[cap]])
        ]

    def satisfies(self, requirements):
        if not hasattr(self, "capabilities"):
            self.setup_capabilities()

        return set(requirements) <= set(self.capabilities)

    def get_info(self):
        url = "/contegoinfo"
        res = self.api.client.get(url)[1]
        return res

    def get_service_list(self):
        url = "/os-services"
        res = self.api.client.get(url)
        return res

    def vast_prepare(self, ctx, server, params):
        header, info = self._action("contego_vast_prepare", base.getid(server), params)
        return info

    def vast_freeze(self, server, params):
        header, info = self._action("contego_vast_freeze", base.getid(server), params)
        return info

    def vast_thaw(self, server, params):
        header, info = self._action("contego_vast_thaw", base.getid(server), params)
        return info

    def vast_instance(self, server, params):
        header, info = self._action("contego_vast_instance", base.getid(server), params)
        return info

    def vast_get_info(self, server, params):
        header, info = self._action("contego_vast_get_info", base.getid(server), params)
        return info

    def vast_data_url(self, server, params):
        header, info = self._action("contego_vast_data_url", base.getid(server), params)
        return info

    def vast_data_transfer(self, server, params, do_checksum=True):
        header, info = self._action(
            "contego_vast_data_transfer", base.getid(server), params
        )
        return info

    def vast_check_prev_snapshot(self, server, params, do_checksum=True):
        header, info = self._action(
            "contego_vast_check_prev_snapshot", base.getid(server), params
        )
        return info

    def vast_async_task_status(self, server, params, do_checksum=True):
        header, info = self._action(
            "contego_vast_async_task_status", base.getid(server), params
        )
        return info

    def vast_finalize(self, server, params):
        header, info = self._action("contego_vast_finalize", base.getid(server), params)
        return info

    def vast_reset(self, server, params):
        header, info = self._action("contego_vast_reset", base.getid(server), params)
        return info

    def map_snapshot_files(self, server, params):
        header, info = self._action(
            "contego_map_snapshot_files", base.getid(server), params
        )
        return info

    def copy_backup_image_to_volume(self, server, params, do_checksum=True):
        header, info = self._action(
            "contego_copy_backup_image_to_volume", base.getid(server), params
        )
        return info

    def testbubble_attach_volume(self, server, params):
        header, info = self._action(
            "contego_testbubble_attach_volume", base.getid(server), params
        )
        return info

    def testbubble_reboot_instance(self, server, params):
        header, info = self._action(
            "contego_testbubble_reboot_instance", base.getid(server), params
        )
        return info

    def vast_commit_image(self, server, params):
        header, info = self._action(
            "contego_vast_commit_image", base.getid(server), params
        )
        return info

    def vast_config_backup(self, backup_id, params):
        header, info = self._action("contego_vast_config_backup", backup_id, params)
        return info

    def vast_disk_check(self, server, params):
        header, info = self._action("contego_vast_disk_check", base.getid(server), params)
        return info

    def vast_clean_nbd_devices(self, ctx, server, params):
        header, info = self._action("contego_vast_clean_nbd_devices", base.getid(server), params)
        return info

    def validate_database_creds(self, config_workload_id, params):
        header, info = self._action(
            "contego_validate_database_creds", config_workload_id, params
        )
        return info

    def validate_trusted_user_and_key(self, config_workload_id, params):
        header, info = self._action(
            "contego_validate_trusted_user_and_key", config_workload_id, params
        )
        return info

    def get_controller_nodes(self, config_workload_id):
        header, info = self._action("contego_get_controller_nodes", config_workload_id)
        return info

    def _action(self, action, server, info=None, retry=0, **kwargs):

        """
        Perform a server "action" -- reboot/rebuild/resize/etc.
        """

        body = {action: info}
        self.run_hooks("modify_body_for_action", body, **kwargs)
        url = "%s/%s" % (action, base.getid(server))
        try:
            return self.post(url, body=body)
        except Exception as ex:
            if retry >= 1:
                raise ex
            return self._action(action, server, info=info, retry=retry + 1, **kwargs)

    def post(self, url, **kwargs):
        return self._cs_request(url, "POST", **kwargs)

    def _cs_request(self, url, method, **kwargs):
        management_url = self.api.client.management_url
        if not management_url:
            self.api.client.authenticate()
        if url is None:
            # To get API version information, it is necessary to GET
            # a dmapi endpoint directly without "v2/<tenant-id>".
            magic_tuple = parse.urlsplit(management_url)
            scheme, netloc, path, query, frag = magic_tuple
            path = re.sub(r"v[1-9]/[a-z0-9]+$", "", path)
            url = parse.urlunsplit((scheme, netloc, path, None, None))
        else:
            if self.api.client.service_catalog:
                s_type = self.api.client.service_type
                s_url = self.api.client.get_service_url(s_type)
                url = os.path.join(self.api.client.get_service_url(s_url, url))
            else:
                # NOTE(melwitt): The service catalog is not available
                #                when bypass_url is used.
                url = os.path.join(management_url, url)

        # 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:
            kwargs.setdefault("headers", {})[
                "X-Auth-Token"
            ] = self.api.client.auth_token
            if self.api.client.projectid:
                kwargs["headers"]["X-Auth-Project-Id"] = self.api.client.projectid
            resp, body = self._time_request(url, method, **kwargs)
            return resp, body
        except exceptions.Unauthorized as e:
            try:
                # first discard auth token, to avoid the possibly expired
                # token being re-used in the re-authentication attempt
                self.api.client.unauthenticate()
                # overwrite bad token
                self.keyring_saved = False
                self.api.client.authenticate()
                kwargs["headers"]["X-Auth-Token"] = self.api.client.auth_token
                resp, body = self._time_request(url, method, **kwargs)
                return resp, body
            except exceptions.Unauthorized:
                raise e

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

    def request(self, url, method, **kwargs):
        kwargs.setdefault("headers", kwargs.get("headers", {}))
        kwargs["headers"]["User-Agent"] = self.api.client.USER_AGENT
        kwargs["headers"]["Accept"] = "application/json"
        if "body" in kwargs:
            kwargs["headers"]["Content-Type"] = "application/json"
            kwargs["data"] = json.dumps(kwargs["body"])
            del kwargs["body"]
        self.timeout = None
        if self.timeout is not None:
            kwargs.setdefault("timeout", self.timeout)
        kwargs["verify"] = self.api.client.verify_cert
        self.api.client.http_log_req(method, url, kwargs)
        request_func = requests.request
        session = self.api.client._get_session(url)
        if session:
            request_func = session.request
        resp = request_func(method, url, **kwargs)
        self.api.client.http_log_resp(resp)

        if resp.text:
            # TODO(dtroyer): verify the note below in a requests context
            # NOTE(alaski): Because force_exceptions_to_status_code=True
            # httplib2 returns a connection refused event as a 400 response.
            # To determine if it is a bad request or refused connection we need
            # to check the body.  httplib2 tests check for 'Connection refused'
            # or actively refused' in the body, so that's what we'll do.
            if resp.status_code == 400:
                if "Connection refused" in resp.text or "actively refused" in resp.text:
                    raise exceptions.ConnectionRefused(resp.text)
            try:
                body = json.loads(resp.text)
            except ValueError:
                body = None
        else:
            body = None

        if resp.status_code >= 400:
            raise exceptions.from_response(resp, body, url, method)

        return resp, body