diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/keystone_listener.py b/sysinv/sysinv/sysinv/sysinv/conductor/keystone_listener.py index 4abe47714f..7f14af30d5 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/keystone_listener.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/keystone_listener.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2019 Intel Corporation +# Copyright (c) 2021 Wind River Systems, Inc. # """ Sysinv Keystone notification listener. @@ -9,7 +10,6 @@ Sysinv Keystone notification listener. import keyring import oslo_messaging - from oslo_config import cfg from oslo_log import log @@ -19,7 +19,8 @@ from sysinv.db import api as dbapi LOG = log.getLogger(__name__) -kube_app = None +callback_func = None +context = None class NotificationEndpoint(object): @@ -38,8 +39,15 @@ class NotificationEndpoint(object): def info(self, ctxt, publisher_id, event_type, payload, metadata): """Receives notification at info level.""" - global kube_app - kube_app.audit_local_registry_secrets() + global callback_func + global context + + if payload['eventType'] == 'activity' and \ + payload['action'] == 'updated.user' and \ + payload['outcome'] == 'success' and \ + payload['resource_info'] == context.user: + callback_func(context) + return oslo_messaging.NotificationResult.HANDLED @@ -57,14 +65,19 @@ def get_transport_url(): auth_password = keyring.get_password('amqp', 'rabbit') - transport_url = "rabbit://guest:%s@%s:5672" % (auth_password, address.address) + if utils.is_valid_ipv6(address.address): + address = "[%s]" % address + + transport_url = "rabbit://guest:%s@%s:5672" % (auth_password, address) return transport_url -def start_keystone_listener(app): +def start_keystone_listener(func, ctxt): - global kube_app - kube_app = app + global callback_func + global context + callback_func = func + context = ctxt conf = cfg.ConfigOpts() conf.transport_url = get_transport_url() diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/kube_app.py b/sysinv/sysinv/sysinv/sysinv/conductor/kube_app.py index a909ee1432..b1f5851c58 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/kube_app.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/kube_app.py @@ -957,7 +957,7 @@ class AppOperator(object): except Exception as e: LOG.exception(e) - def audit_local_registry_secrets(self): + def audit_local_registry_secrets(self, context): """ local registry uses admin's username&password for authentication. K8s stores the authentication info in secrets in order to access diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index 979df1a962..2116b713f3 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -258,7 +258,19 @@ class ConductorManager(service.PeriodicService): # monitor keystone user update event to check whether admin password is # changed or not. If changed, then sync it to kubernetes's secret info. - greenthread.spawn(keystone_listener.start_keystone_listener, self._app) + + admin_user = self._openstack.get_keystone_admin_user() + + if admin_user: + admin_context = ctx.RequestContext(user=admin_user.id, tenant='admin', is_admin=True) + # The thread for kube_app auditing local registry secrets + greenthread.spawn(keystone_listener.start_keystone_listener, + self._app.audit_local_registry_secrets, admin_context) + + # The thread for updating keystone admin password in config files + greenthread.spawn(keystone_listener.start_keystone_listener, + self.update_keystone_password, + admin_context) # Monitor ceph to become responsive if StorageBackendConfig.has_backend_configured( @@ -1641,6 +1653,19 @@ class ConductorManager(service.PeriodicService): return False + def update_keystone_password(self, context): + """This method calls a puppet class + 'openstack::keystone::password::runtime' + on keystone password change""" + + personalities = [constants.CONTROLLER] + config_uuid = self._config_update_hosts(context, personalities) + config_dict = { + "personalities": personalities, + "classes": ["openstack::keystone::password::runtime"] + } + self._config_apply_runtime_manifest(context, config_uuid, config_dict) + def update_remotelogging_config(self, context): """Update the remotelogging configuration""" @@ -5823,7 +5848,7 @@ class ConductorManager(service.PeriodicService): # Audit kubernetes local registry secrets info LOG.debug("Sysinv Conductor running periodic audit task for k8s local registry secrets.") if self._app: - self._app.audit_local_registry_secrets() + self._app.audit_local_registry_secrets(context) @periodic_task.periodic_task(spacing=CONF.conductor.audit_interval) def _conductor_audit(self, context): diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/openstack.py b/sysinv/sysinv/sysinv/sysinv/conductor/openstack.py index ef3ac4bf72..11f9e95ea3 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/openstack.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/openstack.py @@ -347,6 +347,31 @@ class OpenStackOperator(object): "snapshots before restoring volumes.")) LOG.debug("Cinder DB ready for volume Restore") + ######################## + # keystone user methods + ######################## + def _get_keystone_users(self): + """Get a list of all users in keystone otherwise an empty list.""" + user_list = [] + + try: + user_list = self._get_keystone_client(OPENSTACK_CONFIG).users.list() + except Exception as e: + LOG.error("Failed to get keystone user list:\n%s" % str(e)) + + return user_list + + def get_keystone_admin_user(self): + """Return keystone admin otherwise None.""" + users = self._get_keystone_users() + + try: + return [user for user in users if user.name == 'admin'][0] + except Exception as e: + LOG.error("Failed to get keystone admin user:\n%s" % str(e)) + + return None + ######################### # Primary Region Sysinv # Region specific methods