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
import urllib.parse 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 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_instance_user_data(self, server, params):
        header, info = self._action(
            "contego_vast_get_instance_user_data",
            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 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