Files
distcloud/distributedcloud/dccommon/drivers/openstack/sdk_platform.py
Christopher Souza a7ca6368cd Trigger subcloud audits when identity status is updated
In this commit, the trigger of the audits were removed from the manage
action and moved to trigger only when the identity endpoint is updated
from unknown to other statuses. This was done to avoid a race condition
between dcdbsync and the subcloud audit where the subcloud audit would
fail to role not found.
The first_identity_sync_complete field was add to not allow the audit
of any endpoint to be done before the first identity sync is complete,
in order to avoid keystone errors.

Test Plan:
PASS: Add 250 subclouds and manage them all at the same time
and verify that all subclouds went in-sync without errors from
any endpoint audit.
PASS: Unmanage a subcloud and manage it back and verify that
the subcloud is in-sync
PASS: Create a role to update the identity status from in-sync -> out-of-sync
-> in-sync and verify that no extra subcloud audits were triggered.
PASS: Re-install a subcloud that was managed and then manage that
subcloud and verify that the others endpoints audit were only triggered
after the identity sync was complete

Closes-Bug: 2019857

Signed-off-by: Christopher Souza <Christopher.DeOliveiraSouza@windriver.com>
Change-Id: Ied7a3ed99112daeb7af9dd39ad7780c60f451e0e
2023-05-22 19:50:38 -03:00

246 lines
11 KiB
Python

# Copyright 2017-2023 Wind River Inc
# 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.
"""
OpenStack Driver
"""
import collections
from keystoneauth1 import exceptions as keystone_exceptions
from oslo_concurrency import lockutils
from oslo_log import log
from dccommon import consts
from dccommon.drivers.openstack.barbican import BarbicanClient
from dccommon.drivers.openstack.fm import FmClient
from dccommon.drivers.openstack.keystone_v3 import KeystoneClient
from dccommon.drivers.openstack.sysinv_v1 import SysinvClient
from dccommon import exceptions
from dccommon.utils import is_token_expiring_soon
from dcdbsync.dbsyncclient.client import Client as dbsyncclient
KEYSTONE_CLIENT_NAME = 'keystone'
SYSINV_CLIENT_NAME = 'sysinv'
FM_CLIENT_NAME = 'fm'
BARBICAN_CLIENT_NAME = 'barbican'
DBSYNC_CLIENT_NAME = 'dbsync'
LOG = log.getLogger(__name__)
LOCK_NAME = 'dc-openstackdriver-platform'
SUPPORTED_REGION_CLIENTS = [
SYSINV_CLIENT_NAME,
FM_CLIENT_NAME,
BARBICAN_CLIENT_NAME,
DBSYNC_CLIENT_NAME,
]
# region client type and class mappings
region_client_class_map = {
SYSINV_CLIENT_NAME: SysinvClient,
FM_CLIENT_NAME: FmClient,
BARBICAN_CLIENT_NAME: BarbicanClient,
DBSYNC_CLIENT_NAME: dbsyncclient,
}
class OpenStackDriver(object):
os_clients_dict = collections.defaultdict(dict)
_identity_tokens = {}
def __init__(self, region_name=consts.CLOUD_0, thread_name='dcorch',
auth_url=None, region_clients=SUPPORTED_REGION_CLIENTS,
endpoint_type=consts.KS_ENDPOINT_DEFAULT):
# Check if objects are cached and try to use those
self.region_name = region_name
self.keystone_client = None
self.sysinv_client = None
self.fm_client = None
self.barbican_client = None
self.dbsync_client = None
if region_clients:
# check if the requested clients are in the supported client list
result = all(c in SUPPORTED_REGION_CLIENTS for c in region_clients)
if not result:
message = ("Requested clients are not supported: %s" %
' '.join(region_clients))
LOG.error(message)
raise exceptions.InvalidInputError
self.get_cached_keystone_client(region_name)
if self.keystone_client is None:
LOG.debug("get new keystone client for subcloud %s", region_name)
try:
self.keystone_client = KeystoneClient(region_name, auth_url)
except keystone_exceptions.ConnectFailure as exception:
LOG.error('keystone_client region %s error: %s' %
(region_name, str(exception)))
raise exception
except keystone_exceptions.ConnectTimeout as exception:
LOG.debug('keystone_client region %s error: %s' %
(region_name, str(exception)))
raise exception
except keystone_exceptions.NotFound as exception:
LOG.debug('keystone_client region %s error: %s' %
(region_name, str(exception)))
raise exception
except Exception as exception:
LOG.error('keystone_client region %s error: %s' %
(region_name, str(exception)))
raise exception
OpenStackDriver.update_region_clients(region_name,
KEYSTONE_CLIENT_NAME,
self.keystone_client)
# Clear client object cache
if region_name != consts.CLOUD_0:
OpenStackDriver.os_clients_dict[region_name] = \
collections.defaultdict(dict)
if region_clients:
self.get_cached_region_clients_for_thread(region_name,
thread_name,
region_clients)
for client_name in region_clients:
client_obj_name = client_name + '_client'
if getattr(self, client_obj_name) is None:
# Create new client object and cache it
try:
# Since SysinvClient (cgtsclient) does not support session, also pass
# the cached endpoint so it does not need to retrieve it from keystone.
if client_name == "sysinv":
sysinv_endpoint = self.keystone_client.endpoint_cache.get_endpoint('sysinv')
client_object = region_client_class_map[client_name](
region=region_name,
session=self.keystone_client.session,
endpoint_type=endpoint_type,
endpoint=sysinv_endpoint)
else:
client_object = region_client_class_map[client_name](
region=region_name,
session=self.keystone_client.session,
endpoint_type=endpoint_type)
setattr(self, client_obj_name, client_object)
OpenStackDriver.update_region_clients(region_name,
client_name,
client_object,
thread_name)
except Exception as exception:
LOG.error('Region %s client %s thread %s error: %s' %
(region_name, client_name, thread_name,
str(exception)))
raise exception
@lockutils.synchronized(LOCK_NAME)
def get_cached_keystone_client(self, region_name):
if ((region_name in OpenStackDriver.os_clients_dict) and
(KEYSTONE_CLIENT_NAME in
OpenStackDriver.os_clients_dict[region_name]) and
self._is_token_valid(region_name)):
self.keystone_client = (OpenStackDriver.os_clients_dict
[region_name][KEYSTONE_CLIENT_NAME])
@lockutils.synchronized(LOCK_NAME)
def get_cached_region_clients_for_thread(self, region_name, thread_name,
clients):
if ((region_name in OpenStackDriver.os_clients_dict) and
(thread_name in OpenStackDriver.os_clients_dict[
region_name])):
for client in clients:
if client in (OpenStackDriver.os_clients_dict[region_name]
[thread_name]):
LOG.debug('Using cached OS %s client objects %s %s' %
(client, region_name, thread_name))
client_obj = (OpenStackDriver.os_clients_dict[region_name]
[thread_name][client])
setattr(self, client + '_client', client_obj)
else:
OpenStackDriver.os_clients_dict[region_name][thread_name] = {}
@classmethod
@lockutils.synchronized(LOCK_NAME)
def update_region_clients(cls, region_name, client_name, client_object,
thread_name=None):
if thread_name is not None:
cls.os_clients_dict[region_name][thread_name][client_name] = \
client_object
else:
cls.os_clients_dict[region_name][client_name] = client_object
@classmethod
@lockutils.synchronized(LOCK_NAME)
def delete_region_clients(cls, region_name, clear_token=False):
LOG.warn("delete_region_clients=%s, clear_token=%s" %
(region_name, clear_token))
if region_name in cls.os_clients_dict:
del cls.os_clients_dict[region_name]
if clear_token:
cls._identity_tokens[region_name] = None
@classmethod
@lockutils.synchronized(LOCK_NAME)
def delete_region_clients_for_thread(cls, region_name, thread_name):
LOG.debug("delete_region_clients=%s, thread_name=%s" %
(region_name, thread_name))
if (region_name in cls.os_clients_dict and
thread_name in cls.os_clients_dict[region_name]):
del cls.os_clients_dict[region_name][thread_name]
def _is_token_valid(self, region_name):
try:
keystone = \
OpenStackDriver.os_clients_dict[region_name]['keystone'].\
keystone_client
if (not OpenStackDriver._identity_tokens
or region_name not in OpenStackDriver._identity_tokens
or not OpenStackDriver._identity_tokens[region_name]):
OpenStackDriver._identity_tokens[region_name] = \
keystone.tokens.validate(keystone.session.get_token(),
include_catalog=False)
LOG.info("Token for subcloud %s expires_at=%s" %
(region_name,
OpenStackDriver._identity_tokens[region_name]
['expires_at']))
except Exception as exception:
LOG.info('_is_token_valid handle: region: %s error: %s' %
(region_name, str(exception)))
# Reset the cached dictionary
OpenStackDriver.os_clients_dict[region_name] = \
collections.defaultdict(dict)
OpenStackDriver._identity_tokens[region_name] = None
return False
token_expiring_soon = is_token_expiring_soon(
token=self._identity_tokens[region_name])
# If token is expiring soon, reset cached dictionaries and return False.
# Else return true.
if token_expiring_soon:
LOG.info("The cached keystone token for subcloud %s "
"will expire soon %s" %
(region_name,
OpenStackDriver._identity_tokens[region_name]
['expires_at']))
# Reset the cached dictionary
OpenStackDriver.os_clients_dict[region_name] = \
collections.defaultdict(dict)
OpenStackDriver._identity_tokens[region_name] = None
return False
else:
return True