Repository URL to install this package:
| 
          
        
        Version: 
           
    
          6.0.1.dev10  ▾
        
   | 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2013 TrilioData, Inc.
# All Rights Reserved.
"""
Handles all requests relating to compute + nova.
"""
import time
import glob
import itertools
import inspect
import pkgutil
import os
import imp
import pkg_resources
import six
from six.moves.urllib import parse
from threading import Lock
from collections import namedtuple
from functools import wraps
from oslo_config import cfg
from oslo_serialization import base64
from novaclient import exceptions as nova_exception
from novaclient import extension as nova_extension
from novaclient import client as nova_client
from neutronclient.common import exceptions as nc_exc
from keystoneclient import exceptions as keystone_exceptions
from keystoneauth1.identity import v3
from keystoneauth1 import session
from workloadmgr.db import base
from workloadmgr.db.workloadmgrdb import WorkloadMgrDB
from workloadmgr.common import context as wlm_context
from workloadmgr import exception
from workloadmgr.openstack.common.gettextutils import _
from workloadmgr.openstack.common import excutils
from workloadmgr.openstack.common import log as logging
from workloadmgr.common import clients
from workloadmgr import autolog
from workloadmgr.decorators import retry
LOG = logging.getLogger(__name__)
Logger = autolog.Logger(LOG)
nova_opts = [
    cfg.StrOpt('nova_admin_auth_url',
               default='http://localhost:5000/v2.0',
               help='auth url for connecting to nova in admin context'),
    cfg.StrOpt('nova_admin_username',
               default='admin',
               help='tenant name for connecting to nova in admin context'),
    cfg.StrOpt('nova_admin_password',
               default='password',
               help='password for connecting to nova in admin context',
               secret=True),
    cfg.StrOpt('nova_admin_tenant_name',
               default='admin',
               help='tenant name for connecting to nova in admin context'),
    cfg.StrOpt('nova_production_endpoint_template',
               default='http://localhost:8774/v2/%(project_id)s',
               help='nova production endpoint e.g. http://localhost:8774/v2/%(project_id)s'),
    cfg.StrOpt('nova_tvault_endpoint_template',
               default='http://localhost:8774/v2/%(project_id)s',
               help='nova tvault endpoint e.g. http://localhost:8774/v2/%(project_id)s'),
    cfg.StrOpt('nova_production_region_name',
               default=None,
               help='region name for connecting to nova in admin context'),
    cfg.StrOpt('nova_tvault_region_name',
               default=None,
               help='region name for connecting to nova in admin context'),
    cfg.BoolOpt('nova_api_insecure',
                default=True,
                help='if set, ignore any SSL validation issues'),
    cfg.StrOpt('nova_auth_system',
               default='keystone',
               help='auth system for connecting to '
                    'nova in admin context'),
    cfg.IntOpt('nova_url_timeout',
               default=600,
               help='timeout value for connecting to nova in seconds'),
    cfg.StrOpt('cloud_admin_user_id',
               default='admin',
               help='cloud admin user id for cloud admin context.'),
    cfg.StrOpt('cloud_admin_project_id',
               default='admin',
               help='project id for connecting to cloud admin context',
               secret=True),
    cfg.StrOpt('cloud_admin_domain',
               default='default',
               help='domain name for connecting to cloud admin context'),
    cfg.FloatOpt('nova_api_version',
               default=2,
               help='Nova API version used to initialize the nova client.'),
]
CONF = cfg.CONF
CONF.register_opts(nova_opts)
LOG = logging.getLogger(__name__)
novalock = Lock()
class ObjectDummy(object):
    pass
def synchronized(lock):
    """ Synchronization decorator. """
    def wrap(f):
        def new_function(*args, **kw):
            lock.acquire()
            try:
                return f(*args, **kw)
            finally:
                lock.release()
        return new_function
    return wrap
def _discover_extensions(version):
    extensions = []
    for name, module in itertools.chain(
            _discover_via_python_path(),
            _discover_via_contrib_path(version),
            _discover_via_entry_points()):
        extension = nova_extension.Extension(name, module)
        extensions.append(extension)
    return extensions
def _discover_via_python_path():
    for (module_loader, name, _ispkg) in pkgutil.iter_modules():
        if name.endswith('_python_novaclient_ext'):
            if not hasattr(module_loader, 'load_module'):
                # Python 2.6 compat: actually get an ImpImporter obj
                module_loader = module_loader.find_module(name)
            module = module_loader.load_module(name)
            if hasattr(module, 'extension_name'):
                name = module.extension_name
            yield name, module
def _discover_via_contrib_path(version):
    module_path = os.path.dirname(os.path.abspath(__file__))
    version_str = "v%s" % version.replace('.', '_')
    ext_path = os.path.join(module_path, version_str, 'contrib')
    ext_glob = os.path.join(ext_path, "*.py")
    for ext_path in glob.iglob(ext_glob):
        name = os.path.basename(ext_path)[:-3]
        if name == "__init__":
            continue
        module = imp.load_source(name, ext_path)
        yield name, module
def _discover_via_entry_points():
    for ep in pkg_resources.iter_entry_points('novaclient.extension'):
        name = ep.name
        module = ep.load()
        yield name, module
try:
    # load keystone_authtoken by importing keystonemiddleware
    # if it is already loaded, just ignore the exception
    cfg.CONF.import_group('keystone_authtoken',
                          'keystonemiddleware.auth_token')
except BaseException:
    pass
def _get_trusts(user_id, tenant_id):
    db = WorkloadMgrDB().db
    context = wlm_context.RequestContext(
        user_id=user_id,
        project_id=tenant_id)
    settings = db.setting_get_all_by_project(
        context, context.project_id)
    trust = [t for t in settings if t.type == "trust_id" and
             t.project_id == context.project_id and
             t.user_id == context.user_id]
    return trust
def _get_tenant_context(context, cloud_admin=False):
    def _get_context(trust, tenant_id, trustor_user_id, user_domain_id, roles=[]):
        try:
            trust_id = trust[0].value
            req_context = wlm_context.RequestContext(
                username=CONF.keystone_authtoken.admin_user,
                password=CONF.keystone_authtoken.admin_password,
                trust_id=trust_id,
                tenant_id=tenant_id,
                trustor_user_id=trustor_user_id,
                user_domain_id=user_domain_id,
                roles=roles,
                is_admin=False)
            return req_context
        except Exception as ex:
            LOG.exception(ex)
            raise ex
    try:
        from workloadmgr import workloads as workloadAPI
        # Fetch cloud admin trust
        cloud_trust = _get_trusts('cloud_admin', CONF.cloud_admin_project_id)
        if len(cloud_trust) < 1:
            msg = _("No cloud admin trust found. Please recreate using CLI")
            workloadAPI.api.AUDITLOG.log(context, msg, None)
            LOG.exception(msg)
            raise Exception(msg)
        cloud_admin_context = _get_context(cloud_trust, CONF.cloud_admin_project_id,
                                           CONF.cloud_admin_user_id, CONF.cloud_admin_domain)
        try:
            clients.initialise()
            client_plugin = clients.Clients(cloud_admin_context)
            admin_kclient = client_plugin.client("keystone")
        except exception.AuthorizationFailure:
            msg = _("Failed to authenticate using saved cloud admin trust.\
                      Please recreate cloud admin trust using CLI.")
            workloadAPI.api.AUDITLOG.log(context, msg, None)
            LOG.exception(msg)
            raise Exception(msg)
        except Exception as ex:
            LOG.exception(ex)
            raise ex
        if cloud_admin is True:
            cloud_admin_context.auth_token = admin_kclient.auth_token
            cloud_admin_context.user_id = getattr(context, 'user_id', 'NA')
            cloud_admin_context.user = getattr(context, 'user', 'NA')
            cloud_admin_context.tenant = getattr(context, 'tenant', 'NA')
            return cloud_admin_context
        else:
            trust = _get_trusts(context.user_id, context.project_id)
            if len(trust) < 1:
                msg = _("Assign valid trustee role to tenant %s") % getattr(context, 'project_id', 'NA')
                workloadAPI.api.AUDITLOG.log(context, msg, None)
                LOG.info(msg)
                raise Exception(_("token cannot be created using saved "
                                  "trust id for user %s, tenant %s") %
                                (context.user_id, context.project_id))
            roles = getattr(context, 'roles', [])
            if not roles:
                roles = [CONF.trustee_role]
            tenant_context = _get_context(trust, context.project_id, context.user_id, \
                                          CONF.triliovault_user_domain_id, roles=roles)
            clients.initialise()
            client_plugin = clients.Clients(tenant_context)
            try:
                kclient = client_plugin.client("keystone")
            except keystone_exceptions.Forbidden:
                msg = _("Assign valid trustee role to tenant %s") % context.project_id
                raise Exception(msg)
            tenant_context.auth_token = kclient.auth_token
            tenant_context.user_id = context.user_id
            tenant_context.user = getattr(context, 'user', None)
            tenant_context.project_id = getattr(context, 'project_id', None)
            return tenant_context
    except Exception as ex:
        LOG.exception(ex)
        raise ex
def novaclient(context, production=True, refresh_token=False, extensions=None, **kwargs):
    trust = _get_trusts(context.user_id, context.project_id)
    if hasattr(context, 'user_domain_id'):
        if context.user_domain_id is None:
            user_domain_id = 'default'
        else:
            user_domain_id = context.user_domain_id
    elif hasattr(context, 'user_domain'):
        if context.user_domain is None:
            user_domain_id = 'default'
        else:
            user_domain_id = context.user_domain
    else:
        user_domain_id = 'default'
    # pick the first trust. Usually it should not be more than one trust
    if len(trust):
        trust_id = trust[0].value
        if refresh_token:
            context = wlm_context.RequestContext(
                username=CONF.keystone_authtoken.admin_user,
                password=CONF.keystone_authtoken.admin_password,
                trust_id=trust_id,
                tenant_id=context.project_id,
                trustor_user_id=context.user_id,
                user_domain_id=CONF.triliovault_user_domain_id,
                is_admin=False)
        else:
            context = wlm_context.RequestContext(
                trustor_user_id=context.user_id,
                project_id=context.project_id,
                auth_token=context.auth_token,
                trust_id=trust_id,
                user_domain_id=user_domain_id,
                is_admin=False)
        clients.initialise()
        nova_plugin = clients.Clients(context)
        novaclient = nova_plugin.client("nova", **kwargs)
        novaclient.client_plugin = novaclient
    else:
        # trusts are not enabled
        if refresh_token:
            if production:
                url = CONF.nova_production_endpoint_template.replace(
                    '%(project_id)s', context.project_id)
                url = url.replace("/v2.1/", "/v2/")
                novaclient = nova_client.Client(
                    CONF.nova_api_version,
                    CONF.nova_admin_username,
                    CONF.nova_admin_password,
                    project_id=context.project_id,
                    auth_url=CONF.keystone_authtoken.www_authenticate_uri,
                    user_domain_id=user_domain_id,
                    insecure=CONF.nova_api_insecure,
                    extensions=extensions,
                    timeout=CONF.nova_url_timeout,
                    endpoint_type=CONF.clients.endpoint_type)
            else:
                url = CONF.nova_tvault_endpoint_template.replace(
                    '%(project_id)s', context.project_id)
                novaclient = nova_client.Client(
                    CONF.nova_api_version,
                    CONF.nova_admin_username,
                    CONF.nova_admin_password,
                    project_id=context.project_id,
                    auth_url=CONF.keystone_authtoken.www_authenticate_uri,
                    user_domain_id=user_domain_id,
                    insecure=CONF.nova_api_insecure,
                    extensions=extensions,
                    timeout=CONF.nova_url_timeout,
                    endpoint_type=CONF.clients.endpoint_type)
            LOG.debug(_('Novaclient connection created using URL: %s') % url)
        else:
            if production:
                url = CONF.nova_production_endpoint_template % context.to_dict()
                url = url.replace("/v2.1/", "/v2/")
            else:
                url = CONF.nova_tvault_endpoint_template % context.to_dict()
            LOG.debug(_('Novaclient connection created using URL: %s') % url)
            novaclient = nova_client.Client(CONF.nova_api_version,
                                            context.user_id,
                                            context.auth_token,
                                            project_id=context.project_id,
                                            auth_url=CONF.keystone_authtoken.www_authenticate_uri,
                                            user_domain_id=user_domain_id,
                                            insecure=CONF.nova_api_insecure,
                                            extensions=extensions,
                                            timeout=CONF.nova_url_timeout,
                                            endpoint_type=CONF.clients.endpoint_type)
            # noauth extracts user_id:tenant_id from auth_token
            novaclient.client.auth_token = context.auth_token or '%s:%s' % (
                context.user_id, context.project_id)
            novaclient.client.management_url = url
    return novaclient
def novaclient2(auth_url, username, password,
                tenant_name, nova_endpoint_template):
    httpclient = client.HTTPClient(
        user=username,
        password=password,
        projectid=tenant_name,
        service_type='compute',
        endpoint_type=CONF.clients.endpoint_type,
        region_name=CONF.nova_production_region_name,
        auth_url=auth_url,
        timeout=CONF.nova_url_timeout,
        auth_system=CONF.nova_auth_system,
        insecure=CONF.nova_api_insecure)
    httpclient.authenticate()
    url = nova_endpoint_template.replace(
        '%(project_id)s', httpclient.tenant_id)
    c = nova_client.Client(CONF.nova_api_version,
                           username,
                           password,
                           project_id=httpclient.tenant_id,
                           auth_url=url,
                           insecure=CONF.nova_api_insecure,
                           extensions=None,
                           timeout=CONF.nova_url_timeout,
                           endpoint_type=CONF.clients.endpoint_type)
    LOG.debug(_('Novaclient connection created using URL: %s') % url)
    c.client.auth_token = httpclient.auth_token
    c.client.management_url = url
    return c
def nova_micro_client(context, version):
    try:
        user_domain_id = 'default'
        if hasattr(context, 'user_domain_id') and context.user_domain_id:
            user_domain_id = context.user_domain_id
        elif hasattr(context, 'user_domain') and context.user_domain:
            user_domain_id = context.user_domain
        project_domain_id = 'default'
        if hasattr(context, 'project_domain_id') and context.project_domain_id:
            project_domain_id = context.project_domain_id
        elif hasattr(context, 'project_domain') and context.project_domain:
            project_domain_id = context.project_domain
        params = {
             'auth_url': CONF.keystone_authtoken.www_authenticate_uri,
             'region_name': CONF.region_name_for_services,
             'endpoint_type': CONF.clients.endpoint_type,
             'insecure': CONF.clients.insecure,
             'cacert': CONF.clients.cafile,
             'project_domain_id': project_domain_id,
             'user_domain_id': user_domain_id
             }
        if hasattr(context, "password") and context.password:
            params['username'] = context.username
            params['password'] = context.password
        else:
            auth = v3.Token(auth_url=CONF.keystone_authtoken.www_authenticate_uri,
                            project_id=context.project_id,
                            token=context.auth_token)
            sess = session.Session(auth=auth, verify=CONF.clients.cafile)
            params['session'] = sess
        return nova_client.Client(version, **params)
    except nova_exception.Unauthorized as ex:
        LOG.exception(ex)
        raise ex
    except Exception as ex:
        LOG.exception(ex)
        raise ex
def exception_handler(ignore_exception=False,
                      refresh_token=True, contego=False, **kwargs):
    def exception_handler_decorator(func):
        @wraps(func)
        def func_wrapper(*args, **argv):
            try:
                try:
                    extensions = None
                    if contego is True:
                        extensions = _discover_extensions('1.1')
                    client = novaclient(args[1], args[0]._production,
                                        refresh_token=False,
                                        extensions=extensions, **kwargs)
                    argv.update({'client': client})
                    return func(*args, **argv)
                except (nc_exc.NeutronClientException, nova_exception.Unauthorized) as unauth_ex:
                    if refresh_token is True:
                        argv.pop('client')
                        client = novaclient(args[1], args[0]._production,
                                            refresh_token=True,
                                            extensions=extensions)
                        argv.update({'client': client})
                        return func(*args, **argv)
            except Exception as ex:
                if ignore_exception is True:
                    LOG.exception(ex)
                    if nova_exception.BadRequest in \
                            inspect.getmro(ex.__class__) or \
                            nova_exception.NotFound in \
                            inspect.getmro(ex.__class__):
                        return
                    return
                if contego is True:
                    msg = "Unable to call %s; Please check contego " \
                          "logs for more details" % func.__name__
                    if hasattr(ex, 'code') and ex.code == 413:
                        msg = str(ex)
                    raise exception.ErrorOccurred(reason=msg)
                else:
                    raise
        return func_wrapper
    return exception_handler_decorator
class API(base.Base):
    """API for interacting with the volume manager."""
    def __init__(self, production=True):
        self._production = production
    @synchronized(novalock)
    @exception_handler(ignore_exception=True)
    def get_hypervisors(self, context, **kwargs):
        client = kwargs['client']
        hypervisors = novaclient(
            context,
            self._production,
            True).hypervisors.list()
        return hypervisors
    @synchronized(novalock)
    @exception_handler(ignore_exception=False, version='2.60')
    def create_server(
            self,
            context,
            name,
            image,
            flavor,
            meta=None,
            files=None,
            reservation_id=None,
            min_count=None,
            max_count=None,
            security_groups=None,
            userdata=None,
            key_name=None,
            availability_zone=None,
            block_device_mapping=None,
            nics=None,
            scheduler_hints=None,
            **kwargs):
        """
        Create (boot) a new server.
        :param name: Something to name the server.
        :param image: The :class:`Image` to boot with.
        :param flavor: The :class:`Flavor` to boot onto.
        :param production: If True, production Nova will be used.
        :param meta: A dict of arbitrary key/value metadata to store for this
                     server. A maximum of five entries is allowed, and both
                     keys and values must be 255 characters or less.
        :param files: A dict of files to overrwrite on the server upon boot.
                      Keys are file names (i.e. ``/etc/passwd``) and values
                      are the file contents (either as a string or as a
                      file-like object). A maximum of five entries is allowed,
                      and each file must be 10k or less.
        :param userdata: user data to pass to be exposed by the metadata
                      server this can be a file type object as well or a
                      string.
        :param reservation_id: a UUID for the set of servers being requested.
        :param key_name: (optional extension) name of previously created
                      keypair to inject into the instance.
        :param availability_zone: Name of the availability zone for instance
                                  placement.
        :param block_device_mapping: (optional extension) A dict of block
                      device mappings for this server.
        :param nics:  (optional extension) an ordered list of nics to be
                      added to this server, with information about
                      connected networks, fixed ips, port etc.
        :param scheduler_hints: (optional extension) arbitrary key-value pairs
                            specified by the client to help boot an instance
        :param config_drive: (optional extension) value for config drive
                            either boolean, or volume-id
        """
        client = kwargs.pop('client')
        if userdata:
            try:
                userdata = base64.decode_as_text(userdata)
            except Exception as ex:
                LOG.debug("Failed to decode user_data")
                LOG.debug(ex)
        item = client.servers.create(
            name,
            image,
            flavor,
            meta=meta,
            files=files,
            reservation_id=reservation_id,
            min_count=min_count,
            max_count=max_count,
            security_groups=security_groups,
            userdata=userdata,
            key_name=key_name,
            availability_zone=availability_zone,
            block_device_mapping=block_device_mapping,
            nics=nics,
            scheduler_hints=scheduler_hints,
            **kwargs)
        time.sleep(15)
        return item
    def _get_servers(self, context, search_opts=None, admin=False, **kwargs):
        """
        Get all the servers for a particular tenant or all tenants
        :rtype: :class:`Server`
        """
        if search_opts is None:
            search_opts = {}
        if admin:
            search_opts['all_tenants'] = True
        else:
            search_opts['project_id'] = context.project_id
        servers = None
        client = kwargs['client']
        servers = client.servers.list(True, search_opts)
        return servers
    @exception_handler(ignore_exception=False)
    def get_servers(self, context, search_opts=None, admin=False, **kwargs):
        return self._get_servers(context, search_opts, admin, **kwargs)
    @exception_handler(ignore_exception=False)
    def get_server(self, context, name, admin=False, **kwargs):
        """
        Get the server given the name
        :rtype: :class:`Server`
        """
        server = None
        client = kwargs['client']
        client = novaclient(context, self._production, admin)
        return client.servers.find(name=name)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_security_group_by_id(self, context, secid, admin=False, **kwargs):
        """
        Get the security group given the name
        :rtype: :int:`secuirty id`
        """
        client = kwargs['client']
        return client.security_groups.get(secid)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_security_groups(self, context, admin=False, **kwargs):
        """
        Get the security group given the name
        :rtype: :int:`secuirty id`
        """
        client = kwargs['client']
        return client.security_groups.list()
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_server_raw_payload(self, context, id, **kwargs):
        try:
            client = kwargs['client']
            resp, body = client.servers.api.client.get("/servers/%s" % id)
            return resp.text
        except Exception as ex:
            LOG.exception(ex)
            return None
    @exception_handler(ignore_exception=False)
    def get_server_by_id(self, context, id, admin=False,
                         search_opts=None, **kwargs):
        """
        :param id to query.
        :rtype: :class:`Server`
        """
        if search_opts is None:
            servers = self._get_servers(
                context, search_opts, admin=admin, **kwargs)
            for server in servers:
                if server.id == id:
                    return server
            return None
        else:
            qparams = {}
            client = kwargs['client']
            for opt, val in six.iteritems(search_opts):
                if val:
                    qparams[opt] = val
                if qparams:
                    new_qparams = sorted(list(qparams.items()), key=lambda x: x[0])
                    query_string = "?%s" % parse.urlencode(new_qparams)
                else:
                    query_string = ""
                server = client.servers._get(
                    "/servers/%s%s" %
                    (id, query_string), "server")
                return server
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def stop(self, context, server, **kwargs):
        """
        Stop the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        return client.servers.stop(server=server)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def start(self, context, server, **kwargs):
        """
        Start the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        return client.servers.start(server=server)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def suspend(self, context, server, **kwargs):
        """
        Suspend the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        client = novaclient(context, self._production)
        return client.servers.suspend(server=server)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def reboot(self, context, server, reboot_type='SOFT', **kwargs):
        """
        Suspend the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        return client.servers.reboot(server=server, reboot_type=reboot_type)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def resume(self, context, server, **kwargs):
        """
        Resume the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        return client.servers.resume(server=server)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def pause(self, context, server, **kwargs):
        """
        Pause the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        return client.servers.pause(server=server)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def unpause(self, context, server, **kwargs):
        """
        UnPause the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        return client.servers.unpause(server=server)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def delete(self, context, server, **kwargs):
        """
        Delete the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        return client.servers.delete(server=server)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def force_delete(self, context, server, **kwargs):
        """
        Force Delete the server given the id
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        return client.servers.force_delete(server=server)
    @synchronized(novalock)
    @exception_handler(ignore_exception=True)
    def set_meta_item(self, context, server_id, key, value, **kwargs):
        """
        Adds a metadata item to the server given key value
        :param server: The :class:`Server` (or its ID) to query.
        """
        server = namedtuple('server', 'id')
        s = server(id=server_id)
        client = kwargs['client']
        return client.servers.set_meta_item(server=s, key=key, value=value)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def delete_meta(self, context, server_id, keys, **kwargs):
        """
        Delete metadata of the server given the server id and keys
        :param server: The :class:`Server` (or its ID) to query.
        :param keys: meta data keys
        """
        server = namedtuple('server', 'id')
        s = server(id=server_id)
        client = kwargs['client']
        return client.servers.delete_meta(server=s, keys=keys)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def list_security_group(self, context, server_id, **kwargs):
        """
        List security groups on the server
        :param server: The :class:`Server` (or its ID) to query.
        """
        server = namedtuple('server', 'id')
        s = server(id=server_id)
        client = kwargs['client']
        return client.servers.list_security_group(server=s)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def add_security_group(self, context, server_id,
                           security_group_id, **kwargs):
        """
        Add security group identified by security group id
        :param server: The :class:`Server` (or its ID) to query.
        :param security_group_id: Security group id
        """
        server = namedtuple('server', 'id')
        s = server(id=server_id)
        client = kwargs['client']
        return client.servers.add_security_group(
            server=s, security_group=security_group_id)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def remove_security_group(self, context, server_id,
                              security_group_id, **kwargs):
        """
        Removes a security group identified by the security group_id
        :param server: The :class:`Server` (or its ID) to query.
        :param security_group_id: Security group id
        """
        server = namedtuple('server', 'id')
        s = server(id=server_id)
        client = kwargs['client']
        return client.servers.remove_security_group(
            server=s, security_group=security_group_id)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def add_floating_ip(self, context, server_id,
                        floating_ip, fixed_ip, **kwargs):
        """
        Add floating ip to the server
        :param server: The :class:`Server` (or its ID) to query.
        :param floating_ip: Floating IP
        """
        server = namedtuple('server', 'id')
        s = server(id=server_id)
        client = kwargs['client']
        return client.servers.add_floating_ip(server=s, address=floating_ip,
                                              fixed_address=fixed_ip)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def floating_ip_list(self, context, **kwargs):
        """
        Add floating ip to the server
        :param server: The :class:`Server` (or its ID) to query.
        :param floating_ip: Floating IP
        """
        client = kwargs['client']
        return client.floating_ips.list()
    @synchronized(novalock)
    # Support Cinder multi-attach
    @exception_handler(ignore_exception=False, version='2.60') 
    def attach_volume(self, context, server_id, volume_id, device, **kwargs):
        """
        Attach a volume identified by the volume ID to the given server ID
        :param server_id: The ID of the server
        :param volume_id: The ID of the volume to attach.
        :param device: The device name
        :rtype: :class:`Volume`
        """
        client = kwargs['client']
        return client.volumes.create_server_volume(
            server_id, volume_id, device)
    # Support Cinder multi-attach
    @exception_handler(ignore_exception=False, version='2.60')
    def detach_volume(self, context, server_id, volume_id, attachment_id=None, **kwargs):
        """
        Detach a volume identified by the volume ID to the given server ID
        :param server_id: The ID of the server
        :param volume_id: The ID of the volume to attach.
        :rtype: :class:`Volume`
        """
        client = kwargs['client']
        return client.volumes.delete_server_volume(server_id, volume_id, attachment_id)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_image(self, context, id, **kwargs):
        """
        Get the image given the name
        :param name: name of the image
        :rtype: :class:`Image`
        """
        client = kwargs['client']
        return client.glance.find_image(id)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_flavors(self, context, is_public=True, **kwargs):
        """
        Get the list of flavors
        :param is_public: public flavors
        :rtype: :class:`Flavor`
        """
        client = kwargs['client']
        return client.flavors.list(is_public=is_public)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_flavor_by_name(self, context, name, **kwargs):
        """
        Get the flavors given the name
        :param name: name of the flavors
        :rtype: :class:`Flavor`
        """
        client = kwargs['client']
        return client.flavors.find(name=name)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_flavor_by_id(self, context, id, **kwargs):
        """
        Get the flavor given the id
        :param name: id of the flavors
        :rtype: :class:`Flavor`
        """
        client = kwargs['client']
        return client.flavors.get(id)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def create_flavor(self, context, name, memory, vcpus,
                      root_gb, ephemeral_gb, **kwargs):
        """
        Create a new flavor
        :rtype: :class:`Flavor`
        """
        client = kwargs['client']
        return client.flavors.create(name, memory, vcpus, root_gb,
                                     flavorid="auto", ephemeral=ephemeral_gb)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def delete_flavor(self, context, id, **kwargs):
        """
        Delete the falvor given the flavor name
        """
        client = kwargs['client']
        return client.flavors.delete(id)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_keypairs(self, context, **kwargs):
        """
        Get the list of keypairs
        :rtype: :class:`keypair`
        """
        client = kwargs['client']
        return client.keypairs.list()
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def delete_keypair(self, context, name, **kwargs):
        """
        Delete the keypair by name
        """
        client = kwargs['client']
        return client.keypairs.delete(name)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def create_keypair(self, context, name, public_key, key_type, **kwargs):
        """
        Create new keypairs
        """
        user_id = context.user_id if hasattr(context, 'user_id') else None
        # novaclient 2.10 supports to create keypair by providing it's type and user_id
        client = nova_micro_client(context, version='2.10')
        return client.keypairs.create(name, public_key=public_key, key_type=key_type, user_id=user_id)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_keypair_by_name(self, context, name, **kwargs):
        """
        Get the keypair given the name
        :param name: name of the keypair
        :rtype: :class:`keypair`
        """
        user_id = context.user_id if hasattr(context, 'user_id') else None
        # novaclient 2.10 supports to fetch keypair details of specified user_id
        client = nova_micro_client(context, version='2.10')
        return client.keypairs.get(name, user_id=user_id)
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_interfaces(self, context, server, **kwargs):
        """
        List attached network interfaces
        :param server: The :class:`Server` (or its ID) to query.
        """
        client = kwargs['client']
        try:
            return client.servers.interface_list(server=server)
        except nova_exception.HTTPNotImplemented:
            # This is configured to use nova network
            server = client.servers.get(server)
            return server._info['addresses']
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_networks(self, context, **kwargs):
        """
        Get the list of nova networks
        :param is_public: public networks
        :rtype: :class:`Network`
        """
        client = kwargs['client']
        return client.networks.list()
    @synchronized(novalock)
    @exception_handler(ignore_exception=False)
    def get_fixed_ip(self, context, ip, **kwargs):
        """
        Get the IP address information
        :param IP4 address:
        """
        client = kwargs['client']
        return client.fixed_ips.get(ip)
    def contego_service_status(self, context, host=None, ip=None):
        """
        Get contego service status running on a compute node
        """
        contego_service_info = {}
        all_services = []
        try:
            try:
                client = novaclient(context, self._production)
                if host == 'all' and ip == 'all':
                    all_services = client.services.list()
                elif host != 'all':
                    all_services = client.services.list(host=host)
                elif ip != 'all':
                    for hypervisor in client.hypervisors.list():
                        if hypervisor.host_ip == ip:
                            all_services = client.services.list(
                                host=hypervisor.hypervisor_hostname)
                for service in all_services:
                    if service.binary in 'contego':
                        contego_service_info[service.host] = ({"id": service.id,
                                                               "name": service.binary,
                                                               "status": service.status,
                                                               "running_state": service.state
                                                               })
                return contego_service_info
            except nova_exception.Unauthorized as unauth_ex:
                client = novaclient(context, self._production, admin=True)
                all_services = client.services.list()
                for service in all_services:
                    if service.binary in 'contego':
                        contego_service_info[service.host] = ({"id": service.id,
                                                               "name": service.binary,
                                                               "status": service.status,
                                                               "running_state": service.state
                                                               })
                return contego_service_info
            except Exception as ex:
                LOG.exception(ex)
        except Exception as ex:
            LOG.exception(ex)
            msg = 'Unable to get the status of contego service'
            raise exception.ErrorOccurred(msg)
    @synchronized(novalock)
    def get_server_user_data(self, context, server_id, version='2.3'):
        """
        Fetch user_data of the given server id
        :param context: context
        :param server_id: An ID to query.
        :param version: micro version for nova client
        """
        try:
            custom_nova_client = nova_micro_client(context, version)
            server = namedtuple('server', 'id')
            server_obj = server(id=server_id)
            server_obj = custom_nova_client.servers.get(server_obj)
            if server_obj:
                userdata_value = [value for key, value in server_obj.to_dict().items() if key.endswith("user_data")]
                if userdata_value:
                    return userdata_value[0] or None
            return None
        except Exception as ex:
            LOG.exception(ex)
            raise ex
    def get_server(self, context, server_id, version='2.1'):
        """
        Fetch host of the given server id
        :param context: context
        :param server_id: An ID to query.
        :param version: micro version for nova client
        """
        try:
            custom_nova_client = nova_micro_client(context, version)
            server = namedtuple('server', 'id')
            server_obj = server(id=server_id)
            nova_server_obj = None
            try:
                nova_server_obj = custom_nova_client.servers.get(server_obj)
            except nova_exception.NotFound as ex:
                # Case where VM is deleted
                search_opts={'uuid': server_id, 'deleted': True}
                nova_server_obj = custom_nova_client.servers.list(search_opts=search_opts)
                if nova_server_obj:
                    nova_server_obj = nova_server_obj[0]
            if nova_server_obj:
                return nova_server_obj
            else:
                raise Exception("Unable to fetch VM details for VM ID: %s.", server_id)
        except Exception as ex:
            LOG.exception(ex)
            raise ex