Files
distcloud/distributedcloud/dccommon/drivers/openstack/peer_site.py
Zhang Rong(Jon) fe1045afce Add dcmanager client driver support
Add dcmanager client driver to access the peer site dcmanager API

Test Plan:
1. PASS - Verify get subcloud and subcloud list,
          get subcloud peer group and list
2. PASS - Verify add subcloud and add subcloud peer group
3. PASS - Verify delete subcloud and delete subcloud peer group

Call example:
p_ks_client = PeerSiteDriver(
        auth_url=peer.manager_endpoint,
        username=peer.manager_username,
        password=base64.b64decode(
            peer.manager_password.encode("utf-8")).decode("utf-8"),
        site_uuid=peer.peer_uuid)
dc_endpoint = p_ks_client.session.get_endpoint(
        service_type='dcmanager',
        region_name=dccommon_consts.SYSTEM_CONTROLLER_NAME,
        interface=dccommon_consts.KS_ENDPOINT_PUBLIC)
dc_client = DcmanagerClient(dccommon_consts.SYSTEM_CONTROLLER_NAME,
                            p_ks_client.session,
                            endpoint=dc_endpoint)
dc_client.get_subcloud('subcloud1')

Story: 2010852
Task: 48679
Change-Id: I7cdd773ee1238c1e61b5e5d8753ffea9416fcd91
Signed-off-by: Zhang Rong(Jon) <rong.zhang@windriver.com>
2023-09-27 10:59:34 +08:00

199 lines
7.5 KiB
Python

# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
"""
Peer Site OpenStack Driver
"""
import collections
import threading
from keystoneauth1 import loading
from keystoneauth1 import session
from keystoneclient.v3 import client as ks_client
from oslo_concurrency import lockutils
from oslo_log import log
from dccommon import consts
from dccommon.drivers import base
from dccommon import exceptions
from dccommon.utils import is_token_expiring_soon
LOG = log.getLogger(__name__)
LOCK_NAME = 'dc-openstackdriver-peer'
KEYSTONE_CLIENT_NAME = 'keystone'
AUTH_PLUGIN_PASSWORD = 'password'
HTTP_CONNECT_TIMEOUT = 10
class PeerSiteDriver(object):
os_clients_dict = collections.defaultdict(dict)
_identity_tokens = {}
def __init__(self, site_uuid, auth_url, username, password,
region_name=consts.CLOUD_0,
endpoint_type=consts.KS_ENDPOINT_PUBLIC):
if not (site_uuid and auth_url and username and password):
raise exceptions.InvalidInputError
self.site_uuid = site_uuid
self.auth_url = auth_url
self.username = username
self.password = password
self.region_name = region_name
self.endpoint_type = endpoint_type
# Check if objects are cached and try to use those
self.keystone_client = self.get_cached_keystone_client(site_uuid)
if self.keystone_client is None:
LOG.debug("No cached keystone client found. Creating new keystone "
"client for peer site %s", site_uuid)
try:
# Create the keystone client for this site with the provided
# username and password and auth_url.
self.keystone_client = PeerKeystoneClient(
auth_url, username, password,
region_name=region_name,
auth_type=endpoint_type)
except Exception as exception:
LOG.error('peer site %s keystone_client error: %s' %
(site_uuid, str(exception)))
raise exception
# Cache the client object
PeerSiteDriver.update_site_clients(site_uuid,
KEYSTONE_CLIENT_NAME,
self.keystone_client)
@lockutils.synchronized(LOCK_NAME)
def get_cached_keystone_client(self, site_uuid):
if ((site_uuid in PeerSiteDriver.os_clients_dict) and
self._is_token_valid(site_uuid)):
return (PeerSiteDriver.os_clients_dict[site_uuid][
KEYSTONE_CLIENT_NAME])
@classmethod
@lockutils.synchronized(LOCK_NAME)
def update_site_clients(cls, site_uuid, client_name, client_object):
cls.os_clients_dict[site_uuid][client_name] = client_object
@classmethod
@lockutils.synchronized(LOCK_NAME)
def delete_site_clients(cls, site_uuid, clear_token=False):
LOG.warn("delete_site_clients=%s, clear_token=%s" %
(site_uuid, clear_token))
if site_uuid in cls.os_clients_dict:
del cls.os_clients_dict[site_uuid]
if clear_token:
cls._identity_tokens[site_uuid] = None
def _is_token_valid(self, site_uuid):
try:
keystone = PeerSiteDriver.os_clients_dict[site_uuid][
KEYSTONE_CLIENT_NAME].keystone_client
if (not PeerSiteDriver._identity_tokens
or site_uuid not in PeerSiteDriver._identity_tokens
or not PeerSiteDriver._identity_tokens[site_uuid]):
PeerSiteDriver._identity_tokens[site_uuid] = \
keystone.tokens.validate(keystone.session.get_token(),
include_catalog=False)
LOG.info("Token for peer site %s expires_at=%s" %
(site_uuid,
PeerSiteDriver._identity_tokens[site_uuid]
['expires_at']))
except Exception as exception:
LOG.warn('_is_token_valid handle: site: %s error: %s' %
(site_uuid, str(exception)))
# Reset the cached dictionary
PeerSiteDriver.os_clients_dict[site_uuid] = \
collections.defaultdict(dict)
PeerSiteDriver._identity_tokens[site_uuid] = None
return False
token_expiring_soon = is_token_expiring_soon(
token=self._identity_tokens[site_uuid])
# 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 peer site %s "
"will expire soon %s" %
(site_uuid,
PeerSiteDriver._identity_tokens[site_uuid]
['expires_at']))
# Reset the cached dictionary
PeerSiteDriver.os_clients_dict[site_uuid] = \
collections.defaultdict(dict)
PeerSiteDriver._identity_tokens[site_uuid] = None
return False
else:
return True
class PeerKeystoneClient(base.DriverBase):
"""Peer Site Keystone V3 driver."""
plugin_loader = None
plugin_lock = threading.Lock()
def __init__(self, auth_url, username, password,
region_name=consts.CLOUD_0,
project_name=consts.KS_ENDPOINT_PROJECT_DEFAULT,
project_domain_name=consts.KS_ENDPOINT_PROJECT_DOMAIN_DEFAULT,
user_domain_name=consts.KS_ENDPOINT_USER_DOMAIN_DEFAULT,
auth_type=consts.KS_ENDPOINT_PUBLIC):
if not (auth_url and username and password):
raise exceptions.InvalidInputError
self.auth_url = auth_url
self.username = username
self.password = password
self.auth_type = auth_type
self.region_name = region_name
self.project_name = project_name
self.project_domain_name = project_domain_name
self.user_domain_name = user_domain_name
self.session = PeerKeystoneClient.get_admin_session(
self.auth_url,
self.username,
self.user_domain_name,
self.password,
self.project_name,
self.project_domain_name)
self.keystone_client = self._create_keystone_client()
@classmethod
def get_admin_session(cls, auth_url, user_name, user_domain_name,
user_password, user_project, user_project_domain,
timeout=None):
with PeerKeystoneClient.plugin_lock:
if PeerKeystoneClient.plugin_loader is None:
PeerKeystoneClient.plugin_loader = loading.get_plugin_loader(
AUTH_PLUGIN_PASSWORD)
user_auth = PeerKeystoneClient.plugin_loader.load_from_options(
auth_url=auth_url,
username=user_name,
user_domain_name=user_domain_name,
password=user_password,
project_name=user_project,
project_domain_name=user_project_domain,
)
timeout = (HTTP_CONNECT_TIMEOUT if timeout is None else timeout)
return session.Session(
auth=user_auth, additional_headers=consts.USER_HEADER,
timeout=timeout)
def _create_keystone_client(self):
client_kwargs = {
'session': self.session,
'region_name': self.region_name,
'interface': self.auth_type
}
return ks_client.Client(**client_kwargs)