Add nova support in masakari
This patch allows masakari to communicate with nova using novaclient. Change-Id: Ia7a2c8e91811500301d5cc44dfebc9f7ca2026e9
This commit is contained in:
parent
c3a58e4988
commit
484da30293
|
@ -113,6 +113,12 @@ function configure_masakari {
|
||||||
|
|
||||||
configure_auth_token_middleware $MASAKARI_CONF masakari $MASAKARI_AUTH_CACHE_DIR
|
configure_auth_token_middleware $MASAKARI_CONF masakari $MASAKARI_AUTH_CACHE_DIR
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Set os_privileged_user credentials (used for connecting nova service)
|
||||||
|
iniset $MASAKARI_CONF DEFAULT os_privileged_user_name nova
|
||||||
|
iniset $MASAKARI_CONF DEFAULT os_privileged_user_password "$SERVICE_PASSWORD"
|
||||||
|
iniset $MASAKARI_CONF DEFAULT os_privileged_user_tenant "$SERVICE_PROJECT_NAME"
|
||||||
|
iniset $MASAKARI_CONF DEFAULT graceful_shutdown_timeout "$SERVICE_GRACEFUL_SHUTDOWN_TIMEOUT"
|
||||||
}
|
}
|
||||||
|
|
||||||
# install_masakari() - Collect source and prepare
|
# install_masakari() - Collect source and prepare
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Copyright 2016 NTT DATA
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_utils import importutils
|
||||||
|
|
||||||
|
|
||||||
|
def API():
|
||||||
|
cls = importutils.import_class("masakari.compute.nova.API")
|
||||||
|
return cls()
|
|
@ -0,0 +1,239 @@
|
||||||
|
# Copyright 2016 NTT DATA
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Handles all requests to Nova.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from keystoneauth1 import exceptions as keystone_exception
|
||||||
|
import keystoneauth1.loading
|
||||||
|
import keystoneauth1.session
|
||||||
|
from novaclient import api_versions
|
||||||
|
from novaclient import client as nova_client
|
||||||
|
from novaclient import exceptions as nova_exception
|
||||||
|
from novaclient import service_catalog
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import encodeutils
|
||||||
|
from requests import exceptions as request_exceptions
|
||||||
|
import six
|
||||||
|
|
||||||
|
from masakari import conf
|
||||||
|
from masakari import context as ctx
|
||||||
|
from masakari import exception
|
||||||
|
from masakari.i18n import _LI
|
||||||
|
|
||||||
|
CONF = conf.CONF
|
||||||
|
CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
NOVA_API_VERSION = "2.1"
|
||||||
|
|
||||||
|
nova_extensions = [ext for ext in
|
||||||
|
nova_client.discover_extensions(NOVA_API_VERSION)
|
||||||
|
if ext.name in ("list_extensions",)]
|
||||||
|
|
||||||
|
|
||||||
|
def _reraise(desired_exc):
|
||||||
|
six.reraise(type(desired_exc), desired_exc, sys.exc_info()[2])
|
||||||
|
|
||||||
|
|
||||||
|
def translate_nova_exception(method):
|
||||||
|
"""Transforms a cinder exception but keeps its traceback intact."""
|
||||||
|
@functools.wraps(method)
|
||||||
|
def wrapper(self, ctx, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
res = method(self, ctx, *args, **kwargs)
|
||||||
|
except (request_exceptions.Timeout,
|
||||||
|
nova_exception.CommandError,
|
||||||
|
keystone_exception.ConnectionError) as exc:
|
||||||
|
err_msg = encodeutils.exception_to_unicode(exc)
|
||||||
|
_reraise(exception.MasakariException(reason=err_msg))
|
||||||
|
except (keystone_exception.BadRequest,
|
||||||
|
nova_exception.BadRequest) as exc:
|
||||||
|
err_msg = encodeutils.exception_to_unicode(exc)
|
||||||
|
_reraise(exception.InvalidInput(reason=err_msg))
|
||||||
|
except (keystone_exception.Forbidden,
|
||||||
|
nova_exception.Forbidden) as exc:
|
||||||
|
err_msg = encodeutils.exception_to_unicode(exc)
|
||||||
|
_reraise(exception.Forbidden(err_msg))
|
||||||
|
except (nova_exception.EndpointNotFound,
|
||||||
|
nova_exception.NotFound) as exc:
|
||||||
|
err_msg = encodeutils.exception_to_unicode(exc)
|
||||||
|
_reraise(exception.NotFound(reason=err_msg))
|
||||||
|
return res
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def novaclient(context, admin_endpoint=False, privileged_user=False,
|
||||||
|
timeout=None):
|
||||||
|
"""Returns a Nova client
|
||||||
|
|
||||||
|
@param admin_endpoint: If True, use the admin endpoint template from
|
||||||
|
configuration ('nova_endpoint_admin_template' and 'nova_catalog_info')
|
||||||
|
@param privileged_user: If True, use the account from configuration
|
||||||
|
(requires 'os_privileged_user_name', 'os_privileged_user_password' and
|
||||||
|
'os_privileged_user_tenant' to be set)
|
||||||
|
@param timeout: Number of seconds to wait for an answer before raising a
|
||||||
|
Timeout exception (None to disable)
|
||||||
|
"""
|
||||||
|
# FIXME: the novaclient ServiceCatalog object is mis-named.
|
||||||
|
# It actually contains the entire access blob.
|
||||||
|
# Only needed parts of the service catalog are passed in, see
|
||||||
|
# nova/context.py.
|
||||||
|
compat_catalog = {
|
||||||
|
'access': {'serviceCatalog': context.service_catalog or []}
|
||||||
|
}
|
||||||
|
sc = service_catalog.ServiceCatalog(compat_catalog)
|
||||||
|
|
||||||
|
nova_endpoint_template = CONF.nova_endpoint_template
|
||||||
|
nova_catalog_info = CONF.nova_catalog_info
|
||||||
|
|
||||||
|
if admin_endpoint:
|
||||||
|
nova_endpoint_template = CONF.nova_endpoint_admin_template
|
||||||
|
nova_catalog_info = CONF.nova_catalog_admin_info
|
||||||
|
service_type, service_name, endpoint_type = nova_catalog_info.split(':')
|
||||||
|
|
||||||
|
# Extract the region if set in configuration
|
||||||
|
if CONF.os_region_name:
|
||||||
|
region_filter = {'attr': 'region', 'filter_value': CONF.os_region_name}
|
||||||
|
else:
|
||||||
|
region_filter = {}
|
||||||
|
|
||||||
|
if privileged_user and CONF.os_privileged_user_name:
|
||||||
|
context = ctx.RequestContext(
|
||||||
|
CONF.os_privileged_user_name, None,
|
||||||
|
auth_token=CONF.os_privileged_user_password,
|
||||||
|
project_name=CONF.os_privileged_user_tenant,
|
||||||
|
service_catalog=context.service_catalog)
|
||||||
|
|
||||||
|
# When privileged_user is used, it needs to authenticate to Keystone
|
||||||
|
# before querying Nova, so we set auth_url to the identity service
|
||||||
|
# endpoint.
|
||||||
|
if CONF.os_privileged_user_auth_url:
|
||||||
|
url = CONF.os_privileged_user_auth_url
|
||||||
|
else:
|
||||||
|
# We then pass region_name, endpoint_type, etc. to the
|
||||||
|
# Client() constructor so that the final endpoint is
|
||||||
|
# chosen correctly.
|
||||||
|
url = sc.url_for(service_type='identity',
|
||||||
|
endpoint_type=endpoint_type,
|
||||||
|
**region_filter)
|
||||||
|
|
||||||
|
LOG.debug('Creating a Nova client using "%s" user',
|
||||||
|
CONF.os_privileged_user_name)
|
||||||
|
else:
|
||||||
|
if nova_endpoint_template:
|
||||||
|
url = nova_endpoint_template % context.to_dict()
|
||||||
|
else:
|
||||||
|
url = sc.url_for(service_type=service_type,
|
||||||
|
service_name=service_name,
|
||||||
|
endpoint_type=endpoint_type,
|
||||||
|
**region_filter)
|
||||||
|
|
||||||
|
LOG.debug('Nova client connection created using URL: %s', url)
|
||||||
|
|
||||||
|
# Now that we have the correct auth_url, username, password and
|
||||||
|
# project_name, let's build a Keystone session.
|
||||||
|
loader = keystoneauth1.loading.get_plugin_loader(
|
||||||
|
CONF.keystone_authtoken.auth_type)
|
||||||
|
auth = loader.load_from_options(auth_url=url,
|
||||||
|
username=context.user_id,
|
||||||
|
password=context.auth_token,
|
||||||
|
project_name=context.project_name)
|
||||||
|
keystone_session = keystoneauth1.session.Session(auth=auth)
|
||||||
|
|
||||||
|
c = nova_client.Client(api_versions.APIVersion(NOVA_API_VERSION),
|
||||||
|
session=keystone_session,
|
||||||
|
insecure=CONF.nova_api_insecure,
|
||||||
|
timeout=timeout,
|
||||||
|
region_name=CONF.os_region_name,
|
||||||
|
endpoint_type=endpoint_type,
|
||||||
|
cacert=CONF.nova_ca_certificates_file,
|
||||||
|
extensions=nova_extensions)
|
||||||
|
|
||||||
|
if not privileged_user:
|
||||||
|
# noauth extracts user_id:project_id from auth_token
|
||||||
|
c.client.auth_token = (context.auth_token or '%s:%s'
|
||||||
|
% (context.user_id, context.project_id))
|
||||||
|
c.client.management_url = url
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
class API(object):
|
||||||
|
"""API for interacting with novaclient."""
|
||||||
|
|
||||||
|
@translate_nova_exception
|
||||||
|
def get_servers(self, context, host):
|
||||||
|
"""Get a list of servers running on a specified host."""
|
||||||
|
opts = {
|
||||||
|
'host': host,
|
||||||
|
'all_tenants': True
|
||||||
|
}
|
||||||
|
nova = novaclient(context, admin_endpoint=True,
|
||||||
|
privileged_user=True)
|
||||||
|
LOG.info(_LI('Fetch Server list on %s'), host)
|
||||||
|
return nova.servers.list(detailed=True, search_opts=opts)
|
||||||
|
|
||||||
|
@translate_nova_exception
|
||||||
|
def enable_disable_service(self, context, host_name, enable=False,
|
||||||
|
reason=None):
|
||||||
|
"""Enable or disable the service specified by hostname and binary."""
|
||||||
|
nova = novaclient(context, admin_endpoint=True,
|
||||||
|
privileged_user=True)
|
||||||
|
|
||||||
|
if not enable:
|
||||||
|
LOG.info(_LI('Disable nova-compute on %s'), host_name)
|
||||||
|
if reason:
|
||||||
|
nova.services.disable_log_reason(host_name, 'nova-compute',
|
||||||
|
reason)
|
||||||
|
else:
|
||||||
|
nova.services.disable(host_name, 'nova-compute')
|
||||||
|
else:
|
||||||
|
LOG.info(_LI('Enable nova-compute on %s'), host_name)
|
||||||
|
nova.services.enable(host_name, 'nova-compute')
|
||||||
|
|
||||||
|
@translate_nova_exception
|
||||||
|
def evacuate_instance(self, context, uuid, target=None,
|
||||||
|
on_shared_storage=True):
|
||||||
|
"""Evacuate an instance from failed host to specified host."""
|
||||||
|
msg = (_LI('Call evacuate command for instance %(uuid)s on host '
|
||||||
|
'%(target)s'))
|
||||||
|
LOG.info(msg, {'uuid': uuid, 'target': target})
|
||||||
|
nova = novaclient(context, admin_endpoint=True,
|
||||||
|
privileged_user=True)
|
||||||
|
nova.servers.evacuate(uuid, host=target,
|
||||||
|
on_shared_storage=on_shared_storage)
|
||||||
|
|
||||||
|
@translate_nova_exception
|
||||||
|
def reset_instance_state(self, context, uuid, status='error'):
|
||||||
|
"""Reset the state of an instance to active or error."""
|
||||||
|
msg = (_LI('Call reset state command on instance %(uuid)s to '
|
||||||
|
'status: %(status)s.'))
|
||||||
|
LOG.info(msg, {'uuid': uuid, 'status': status})
|
||||||
|
nova = novaclient(context, admin_endpoint=True,
|
||||||
|
privileged_user=True)
|
||||||
|
nova.servers.reset_state(uuid, status)
|
||||||
|
|
||||||
|
@translate_nova_exception
|
||||||
|
def get_server(self, context, uuid):
|
||||||
|
"""Get a server."""
|
||||||
|
nova = novaclient(context, admin_endpoint=True,
|
||||||
|
privileged_user=True)
|
||||||
|
msg = (_LI('Call get server command for instance %(uuid)s'))
|
||||||
|
LOG.info(msg, {'uuid': uuid})
|
||||||
|
return nova.servers.get(uuid)
|
|
@ -20,6 +20,7 @@ from masakari.conf import base
|
||||||
from masakari.conf import database
|
from masakari.conf import database
|
||||||
from masakari.conf import engine
|
from masakari.conf import engine
|
||||||
from masakari.conf import exceptions
|
from masakari.conf import exceptions
|
||||||
|
from masakari.conf import nova
|
||||||
from masakari.conf import osapi_v1
|
from masakari.conf import osapi_v1
|
||||||
from masakari.conf import paths
|
from masakari.conf import paths
|
||||||
from masakari.conf import service
|
from masakari.conf import service
|
||||||
|
@ -33,6 +34,7 @@ base.register_opts(CONF)
|
||||||
database.register_opts(CONF)
|
database.register_opts(CONF)
|
||||||
engine.register_opts(CONF)
|
engine.register_opts(CONF)
|
||||||
exceptions.register_opts(CONF)
|
exceptions.register_opts(CONF)
|
||||||
|
nova.register_opts(CONF)
|
||||||
osapi_v1.register_opts(CONF)
|
osapi_v1.register_opts(CONF)
|
||||||
paths.register_opts(CONF)
|
paths.register_opts(CONF)
|
||||||
ssl.register_opts(CONF)
|
ssl.register_opts(CONF)
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Copyright (c) 2016 NTT DATA
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from keystoneauth1 import loading as ks_loading
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
nova_opts = [
|
||||||
|
cfg.StrOpt('nova_catalog_info',
|
||||||
|
default='compute:Compute Service:publicURL',
|
||||||
|
help='Match this value when searching for nova in the '
|
||||||
|
'service catalog. Format is: separated values of '
|
||||||
|
'the form: '
|
||||||
|
'<service_type>:<service_name>:<endpoint_type>'),
|
||||||
|
cfg.StrOpt('nova_catalog_admin_info',
|
||||||
|
default='compute:Compute Service:adminURL',
|
||||||
|
help='Same as nova_catalog_info, but for admin endpoint.'),
|
||||||
|
cfg.StrOpt('nova_endpoint_template',
|
||||||
|
help='Override service catalog lookup with template for nova '
|
||||||
|
'endpoint e.g. http://localhost:8774/v2/%(project_id)s'),
|
||||||
|
cfg.StrOpt('nova_endpoint_admin_template',
|
||||||
|
help='Same as nova_endpoint_template, but for admin endpoint.'),
|
||||||
|
cfg.StrOpt('os_region_name',
|
||||||
|
help='Region name of this node'),
|
||||||
|
cfg.StrOpt('nova_ca_certificates_file',
|
||||||
|
help='Location of ca certificates file to use for nova client '
|
||||||
|
'requests.'),
|
||||||
|
cfg.BoolOpt('nova_api_insecure',
|
||||||
|
default=False,
|
||||||
|
help='Allow to perform insecure SSL requests to nova'),
|
||||||
|
cfg.StrOpt('os_privileged_user_name',
|
||||||
|
help='OpenStack privileged account username. Used for requests '
|
||||||
|
'to other services (such as Nova) that require an account '
|
||||||
|
'with special rights.'),
|
||||||
|
cfg.StrOpt('os_privileged_user_password',
|
||||||
|
help='Password associated with the OpenStack privileged '
|
||||||
|
'account.',
|
||||||
|
secret=True),
|
||||||
|
cfg.StrOpt('os_privileged_user_tenant',
|
||||||
|
help='Tenant name associated with the OpenStack privileged '
|
||||||
|
'account.'),
|
||||||
|
cfg.URIOpt('os_privileged_user_auth_url',
|
||||||
|
help='Auth URL associated with the OpenStack privileged '
|
||||||
|
'account.'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_opts(nova_opts)
|
||||||
|
ks_loading.register_session_conf_options(conf, 'DEFAULT')
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
'DEFAULT': nova_opts
|
||||||
|
}
|
|
@ -127,7 +127,7 @@ class RequestContext(context.RequestContext):
|
||||||
# Only include required parts of service_catalog
|
# Only include required parts of service_catalog
|
||||||
self.service_catalog = [
|
self.service_catalog = [
|
||||||
s for s in service_catalog if s.get('type') in (
|
s for s in service_catalog if s.get('type') in (
|
||||||
'key-manager')]
|
'compute', 'identity')]
|
||||||
else:
|
else:
|
||||||
# if list is empty or none
|
# if list is empty or none
|
||||||
self.service_catalog = []
|
self.service_catalog = []
|
||||||
|
|
|
@ -155,6 +155,19 @@ class MasakariException(Exception):
|
||||||
return self.args[0]
|
return self.args[0]
|
||||||
|
|
||||||
|
|
||||||
|
class APIException(MasakariException):
|
||||||
|
msg_fmt = _("Error while requesting %(service)s API.")
|
||||||
|
|
||||||
|
def __init__(self, message=None, **kwargs):
|
||||||
|
if 'service' not in kwargs:
|
||||||
|
kwargs['service'] = 'unknown'
|
||||||
|
super(APIException, self).__init__(message, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class APITimeout(APIException):
|
||||||
|
msg_fmt = _("Timeout while requesting %(service)s API.")
|
||||||
|
|
||||||
|
|
||||||
class Invalid(MasakariException):
|
class Invalid(MasakariException):
|
||||||
msg_fmt = _("Bad Request - Invalid Parameters")
|
msg_fmt = _("Bad Request - Invalid Parameters")
|
||||||
code = 400
|
code = 400
|
||||||
|
|
|
@ -19,6 +19,7 @@ oslo.service>=1.10.0 # Apache-2.0
|
||||||
oslo.utils>=3.11.0 # Apache-2.0
|
oslo.utils>=3.11.0 # Apache-2.0
|
||||||
oslo.versionedobjects>=1.13.0 # Apache-2.0
|
oslo.versionedobjects>=1.13.0 # Apache-2.0
|
||||||
pbr>=1.6 # Apache-2.0
|
pbr>=1.6 # Apache-2.0
|
||||||
|
python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
|
||||||
setuptools>=16.0 # PSF/ZPL
|
setuptools>=16.0 # PSF/ZPL
|
||||||
six>=1.9.0 # MIT
|
six>=1.9.0 # MIT
|
||||||
stevedore>=1.10.0 # Apache-2.0
|
stevedore>=1.10.0 # Apache-2.0
|
||||||
|
|
Loading…
Reference in New Issue