From 13427a40768f1a4646520c6b7e3e8c988ce6e18c Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Wed, 17 Dec 2014 13:57:35 +1000 Subject: [PATCH] Make nova notifier work with sessions This allows a variety of additional authentication methods and gives us keystone V3 auth for free. DocImpact: Deprecates the nova_admin_* config options in favour of putting nova config options in the [nova] section. Closes-Bug: #1403686 Change-Id: Ia9941cd3e85f21cbd4db9eb3cef8b200131af9ce --- neutron/common/config.py | 46 ++++++++++++++++++-------- neutron/notifiers/nova.py | 69 ++++++++++++++++++++++++++++++--------- 2 files changed, 86 insertions(+), 29 deletions(-) diff --git a/neutron/common/config.py b/neutron/common/config.py index 080396b7c..696f1c4f9 100644 --- a/neutron/common/config.py +++ b/neutron/common/config.py @@ -20,6 +20,8 @@ Routines for configuring Neutron import os import sys +from keystoneclient import auth +from keystoneclient import session as ks_session from oslo_config import cfg from oslo_db import options as db_options import oslo_messaging @@ -96,27 +98,26 @@ core_opts = [ "floatingip) changes so nova can update its cache.")), cfg.StrOpt('nova_url', default='http://127.0.0.1:8774/v2', - help=_('URL for connection to nova')), + help=_('URL for connection to nova. ' + 'Deprecated in favour of an auth plugin in [nova].')), cfg.StrOpt('nova_admin_username', - help=_('Username for connecting to nova in admin context')), + help=_('Username for connecting to nova in admin context. ' + 'Deprecated in favour of an auth plugin in [nova].')), cfg.StrOpt('nova_admin_password', - help=_('Password for connection to nova in admin context'), + help=_('Password for connection to nova in admin context. ' + 'Deprecated in favour of an auth plugin in [nova].'), secret=True), cfg.StrOpt('nova_admin_tenant_id', - help=_('The uuid of the admin nova tenant')), + help=_('The uuid of the admin nova tenant. ' + 'Deprecated in favour of an auth plugin in [nova].')), cfg.StrOpt('nova_admin_tenant_name', - help=_('The name of the admin nova tenant')), + help=_('The name of the admin nova tenant. ' + 'Deprecated in favour of an auth plugin in [nova].')), cfg.StrOpt('nova_admin_auth_url', default='http://localhost:5000/v2.0', help=_('Authorization URL for connecting to nova in admin ' - 'context')), - cfg.StrOpt('nova_ca_certificates_file', - help=_('CA file for novaclient to verify server certificates')), - cfg.BoolOpt('nova_api_insecure', default=False, - help=_("If True, ignore any SSL validation issues")), - cfg.StrOpt('nova_region_name', - help=_('Name of nova region to use. Useful if keystone manages' - ' more than one region.')), + 'context. ' + 'Deprecated in favour of an auth plugin in [nova].')), cfg.IntOpt('send_events_interval', default=2, help=_('Number of seconds between sending events to nova if ' 'there are any events to send.')), @@ -143,6 +144,25 @@ db_options.set_defaults(cfg.CONF, sqlite_db='', max_pool_size=10, max_overflow=20, pool_timeout=10) +NOVA_CONF_SECTION = 'nova' + +nova_deprecated_opts = { + 'cafile': [cfg.DeprecatedOpt('nova_ca_certificates_file', 'DEFAULT')], + 'insecure': [cfg.DeprecatedOpt('nova_api_insecure', 'DEFAULT')], +} +ks_session.Session.register_conf_options(cfg.CONF, NOVA_CONF_SECTION, + deprecated_opts=nova_deprecated_opts) +auth.register_conf_options(cfg.CONF, NOVA_CONF_SECTION) + +nova_opts = [ + cfg.StrOpt('region_name', + deprecated_name='nova_region_name', + deprecated_group='DEFAULT', + help=_('Name of nova region to use. Useful if keystone manages' + ' more than one region.')), +] +cfg.CONF.register_opts(nova_opts, group=NOVA_CONF_SECTION) + def init(args, **kwargs): cfg.CONF(args=args, project='neutron', diff --git a/neutron/notifiers/nova.py b/neutron/notifiers/nova.py index bf0c3c28d..39b3af785 100644 --- a/neutron/notifiers/nova.py +++ b/neutron/notifiers/nova.py @@ -14,6 +14,9 @@ # under the License. import eventlet +from keystoneclient import auth as ks_auth +from keystoneclient.auth.identity import v2 as v2_auth +from keystoneclient import session as ks_session from novaclient import client as nova_client from novaclient import exceptions as nova_exceptions from oslo_config import cfg @@ -38,16 +41,57 @@ NEUTRON_NOVA_EVENT_STATUS_MAP = {constants.PORT_STATUS_ACTIVE: 'completed', NOVA_API_VERSION = "2" +class DefaultAuthPlugin(v2_auth.Password): + """A wrapper around standard v2 user/pass to handle bypass url. + + This is only necessary because novaclient doesn't support endpoint_override + yet - bug #1403329. + + When this bug is fixed we can pass the endpoint_override to the client + instead and remove this class. + """ + + def __init__(self, **kwargs): + self._endpoint_override = kwargs.pop('endpoint_override', None) + super(DefaultAuthPlugin, self).__init__(**kwargs) + + def get_endpoint(self, session, **kwargs): + if self._endpoint_override: + return self._endpoint_override + + return super(DefaultAuthPlugin, self).get_endpoint(session, **kwargs) + + class Notifier(object): def __init__(self): - # TODO(arosen): we need to cache the endpoints and figure out - # how to deal with different regions here.... - if cfg.CONF.nova_admin_tenant_id: - bypass_url = "%s/%s" % (cfg.CONF.nova_url, - cfg.CONF.nova_admin_tenant_id) - else: - bypass_url = None + # FIXME(jamielennox): A notifier is being created for each Controller + # and each Notifier is handling it's own auth. That means that we are + # authenticating the exact same thing len(controllers) times. This + # should be an easy thing to optimize. + auth = ks_auth.load_from_conf_options(cfg.CONF, 'nova') + endpoint_override = None + + if not auth: + LOG.warning(_LW('Authenticating to nova using nova_admin_* options' + ' is deprecated. This should be done using' + ' an auth plugin, like password')) + + if cfg.CONF.nova_admin_tenant_id: + endpoint_override = "%s/%s" % (cfg.CONF.nova_url, + cfg.CONF.nova_admin_tenant_id) + + auth = DefaultAuthPlugin( + auth_url=cfg.CONF.nova_admin_auth_url, + username=cfg.CONF.nova_admin_username, + password=cfg.CONF.nova_admin_password, + tenant_id=cfg.CONF.nova_admin_tenant_id, + tenant_name=cfg.CONF.nova_admin_tenant_name, + endpoint_override=endpoint_override) + + session = ks_session.Session.load_from_conf_options(cfg.CONF, + 'nova', + auth=auth) # NOTE(andreykurilin): novaclient.v1_1 was renamed to v2 and there is # no way to import the contrib module directly without referencing v2, @@ -58,15 +102,8 @@ class Notifier(object): ".client", ".contrib.server_external_events")) self.nclient = novaclient_cls( - username=cfg.CONF.nova_admin_username, - api_key=cfg.CONF.nova_admin_password, - project_id=cfg.CONF.nova_admin_tenant_name, - tenant_id=cfg.CONF.nova_admin_tenant_id, - auth_url=cfg.CONF.nova_admin_auth_url, - cacert=cfg.CONF.nova_ca_certificates_file, - insecure=cfg.CONF.nova_api_insecure, - bypass_url=bypass_url, - region_name=cfg.CONF.nova_region_name, + session=session, + region_name=cfg.CONF.nova.region_name, extensions=[server_external_events]) self.pending_events = [] self._waiting_to_send = False -- 2.45.2