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>
This commit is contained in:
parent
35d473c04d
commit
fe1045afce
|
@ -16,8 +16,13 @@ SECONDS_IN_HOUR = 3600
|
|||
|
||||
KS_ENDPOINT_ADMIN = "admin"
|
||||
KS_ENDPOINT_INTERNAL = "internal"
|
||||
KS_ENDPOINT_PUBLIC = "public"
|
||||
KS_ENDPOINT_DEFAULT = KS_ENDPOINT_ADMIN
|
||||
|
||||
KS_ENDPOINT_USER_DOMAIN_DEFAULT = "Default"
|
||||
KS_ENDPOINT_PROJECT_DEFAULT = "admin"
|
||||
KS_ENDPOINT_PROJECT_DOMAIN_DEFAULT = "Default"
|
||||
|
||||
ENDPOINT_TYPE_IDENTITY_OS = "identity_openstack"
|
||||
|
||||
# openstack endpoint types
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from oslo_log import log
|
||||
import requests
|
||||
from requests_toolbelt import MultipartEncoder
|
||||
|
||||
from dccommon import consts
|
||||
from dccommon.drivers import base
|
||||
from dccommon import exceptions
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
DCMANAGER_CLIENT_REST_DEFAULT_TIMEOUT = 600
|
||||
|
||||
|
||||
class DcmanagerClient(base.DriverBase):
|
||||
"""Dcmanager V1 driver."""
|
||||
|
||||
def __init__(self, region, session,
|
||||
timeout=DCMANAGER_CLIENT_REST_DEFAULT_TIMEOUT,
|
||||
endpoint_type=consts.KS_ENDPOINT_PUBLIC,
|
||||
endpoint=None):
|
||||
if endpoint is None:
|
||||
endpoint = session.get_endpoint(
|
||||
service_type='dcmanager',
|
||||
region_name=region,
|
||||
interface=endpoint_type)
|
||||
self.endpoint = endpoint
|
||||
self.token = session.get_token()
|
||||
self.timeout = timeout
|
||||
|
||||
def get_subcloud(self, subcloud_ref):
|
||||
"""Get subcloud."""
|
||||
if subcloud_ref is None:
|
||||
raise ValueError("subcloud_ref is required.")
|
||||
url = f"{self.endpoint}/subclouds/{subcloud_ref}"
|
||||
|
||||
headers = {"X-Auth-Token": self.token}
|
||||
response = requests.get(url, headers=headers, timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
if response.status_code == 404 and \
|
||||
'Subcloud not found' in response.text:
|
||||
raise exceptions.SubcloudNotFound(subcloud_ref=subcloud_ref)
|
||||
message = "Get Subcloud: subcloud_ref %s failed with RC: %d" % \
|
||||
(subcloud_ref, response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def get_subcloud_list(self):
|
||||
"""Get subcloud list."""
|
||||
url = self.endpoint + '/subclouds'
|
||||
|
||||
headers = {"X-Auth-Token": self.token}
|
||||
response = requests.get(url, headers=headers, timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return data.get('subclouds', [])
|
||||
else:
|
||||
message = "Get Subcloud list failed with RC: %d" % \
|
||||
response.status_code
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def get_subcloud_group_list(self):
|
||||
"""Get subcloud group list."""
|
||||
url = self.endpoint + '/subcloud-groups'
|
||||
|
||||
headers = {"X-Auth-Token": self.token}
|
||||
response = requests.get(url, headers=headers, timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return data.get('subcloud_groups', [])
|
||||
else:
|
||||
message = "Get Subcloud Group list: failed with RC: %d" % \
|
||||
response.status_code
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def get_subcloud_peer_group_list(self):
|
||||
"""Get subcloud peer group list."""
|
||||
url = self.endpoint + '/subcloud-peer-groups'
|
||||
|
||||
headers = {"X-Auth-Token": self.token}
|
||||
response = requests.get(url, headers=headers, timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return data.get('subcloud_peer_groups', [])
|
||||
else:
|
||||
message = "Get Subcloud Peer Group list: failed with RC: %d" % \
|
||||
response.status_code
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def get_subcloud_peer_group(self, peer_group_ref):
|
||||
"""Get subcloud peer group."""
|
||||
if peer_group_ref is None:
|
||||
raise ValueError("peer_group_ref is required.")
|
||||
url = f"{self.endpoint}/subcloud-peer-groups/{peer_group_ref}"
|
||||
|
||||
headers = {"X-Auth-Token": self.token}
|
||||
response = requests.get(url, headers=headers, timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
if response.status_code == 404 and \
|
||||
'Subcloud Peer Group not found' in response.text:
|
||||
raise exceptions.SubcloudPeerGroupNotFound(
|
||||
peer_group_ref=peer_group_ref)
|
||||
message = "Get Subcloud Peer Group: peer_group_ref %s " \
|
||||
"failed with RC: %d" % (peer_group_ref, response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def get_subcloud_list_by_peer_group(self, peer_group_ref):
|
||||
"""Get subclouds in the specified subcloud peer group."""
|
||||
if peer_group_ref is None:
|
||||
raise ValueError("peer_group_ref is required.")
|
||||
url = f"{self.endpoint}/subcloud-peer-groups/{peer_group_ref}/" \
|
||||
"subclouds"
|
||||
|
||||
headers = {"X-Auth-Token": self.token}
|
||||
response = requests.get(url, headers=headers, timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return data.get('subclouds', [])
|
||||
else:
|
||||
if response.status_code == 404 and \
|
||||
'Subcloud Peer Group not found' in response.text:
|
||||
raise exceptions.SubcloudPeerGroupNotFound(
|
||||
peer_group_ref=peer_group_ref)
|
||||
message = "Get Subcloud list by Peer Group: peer_group_ref %s " \
|
||||
"failed with RC: %d" % (peer_group_ref, response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def add_subcloud_peer_group(self, **kwargs):
|
||||
"""Add a subcloud peer group."""
|
||||
url = self.endpoint + '/subcloud-peer-groups'
|
||||
|
||||
headers = {"X-Auth-Token": self.token,
|
||||
"Content-Type": "application/json"}
|
||||
response = requests.post(url, json=kwargs, headers=headers,
|
||||
timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
message = "Add Subcloud Peer Group: %s, failed with RC: %d" % \
|
||||
(kwargs, response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def add_subcloud_with_secondary_status(self, files, data):
|
||||
"""Add a subcloud with secondary status."""
|
||||
url = self.endpoint + '/subclouds'
|
||||
|
||||
# If not explicitly specified, set 'secondary' to true by default.
|
||||
# This action adds a secondary subcloud with rehoming data in the
|
||||
# peer site without creating an actual subcloud.
|
||||
if 'secondary' in data and data['secondary'] != "true":
|
||||
raise ValueError("secondary in data must true.")
|
||||
data['secondary'] = "true"
|
||||
|
||||
fields = dict()
|
||||
if files is not None:
|
||||
# If files are specified, add them to the fields.
|
||||
for k, v in files.items():
|
||||
fields.update({k: (v, open(v, 'rb'),)})
|
||||
|
||||
fields.update(data)
|
||||
enc = MultipartEncoder(fields=fields)
|
||||
headers = {"X-Auth-Token": self.token,
|
||||
"Content-Type": enc.content_type}
|
||||
response = requests.post(url, headers=headers, data=enc,
|
||||
timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
message = "Add Subcloud with secondary status: files: %s, " \
|
||||
"data: %s, failed with RC: %d" % (files, data,
|
||||
response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def update_subcloud_peer_group(self, peer_group_ref, **kwargs):
|
||||
"""Update the subcloud peer group."""
|
||||
if peer_group_ref is None:
|
||||
raise ValueError("peer_group_ref is required.")
|
||||
url = f"{self.endpoint}/subcloud-peer-groups/{peer_group_ref}"
|
||||
|
||||
headers = {"X-Auth-Token": self.token,
|
||||
"Content-Type": "application/json"}
|
||||
response = requests.patch(url, json=kwargs, headers=headers,
|
||||
timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
if response.status_code == 404 and \
|
||||
'Subcloud Peer Group not found' in response.text:
|
||||
raise exceptions.SubcloudPeerGroupNotFound(
|
||||
peer_group_ref=peer_group_ref)
|
||||
message = "Update Subcloud Peer Group: peer_group_ref %s, %s, " \
|
||||
"failed with RC: %d" % (peer_group_ref, kwargs,
|
||||
response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def update_subcloud(self, subcloud_ref, files, data):
|
||||
"""Update the subcloud."""
|
||||
if subcloud_ref is None:
|
||||
raise ValueError("subcloud_ref is required.")
|
||||
url = f"{self.endpoint}/subclouds/{subcloud_ref}"
|
||||
|
||||
fields = dict()
|
||||
if files is not None:
|
||||
# If files are specified, add them to the fields.
|
||||
for k, v in files.items():
|
||||
fields.update({k: (v, open(v, 'rb'),)})
|
||||
|
||||
fields.update(data)
|
||||
enc = MultipartEncoder(fields=fields)
|
||||
headers = {"X-Auth-Token": self.token,
|
||||
"Content-Type": enc.content_type}
|
||||
response = requests.patch(url, headers=headers, data=enc,
|
||||
timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
if response.status_code == 404 and \
|
||||
'Subcloud not found' in response.text:
|
||||
raise exceptions.SubcloudNotFound(subcloud_ref=subcloud_ref)
|
||||
message = "Update Subcloud: subcloud_ref: %s files: %s, " \
|
||||
"data: %s, failed with RC: %d" % (subcloud_ref, files, data,
|
||||
response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def delete_subcloud_peer_group(self, peer_group_ref):
|
||||
"""Delete the subcloud peer group."""
|
||||
if peer_group_ref is None:
|
||||
raise ValueError("peer_group_ref is required.")
|
||||
url = f"{self.endpoint}/subcloud-peer-groups/{peer_group_ref}"
|
||||
|
||||
headers = {"X-Auth-Token": self.token}
|
||||
response = requests.delete(url, headers=headers,
|
||||
timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
if response.status_code == 404 and \
|
||||
'Subcloud Peer Group not found' in response.text:
|
||||
raise exceptions.SubcloudPeerGroupNotFound(
|
||||
peer_group_ref=peer_group_ref)
|
||||
message = "Delete Subcloud Peer Group: peer_group_ref %s " \
|
||||
"failed with RC: %d" % (peer_group_ref, response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
||||
|
||||
def delete_subcloud(self, subcloud_ref):
|
||||
"""Delete the subcloud."""
|
||||
if subcloud_ref is None:
|
||||
raise ValueError("subcloud_ref is required.")
|
||||
url = f"{self.endpoint}/subclouds/{subcloud_ref}"
|
||||
|
||||
headers = {"X-Auth-Token": self.token}
|
||||
response = requests.delete(url, headers=headers,
|
||||
timeout=self.timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
if response.status_code == 404 and \
|
||||
'Subcloud not found' in response.text:
|
||||
raise exceptions.SubcloudNotFound(subcloud_ref=subcloud_ref)
|
||||
message = "Delete Subcloud: subcloud_ref %s failed with RC: %d" % \
|
||||
(subcloud_ref, response.status_code)
|
||||
LOG.error(message)
|
||||
raise Exception(message)
|
|
@ -0,0 +1,198 @@
|
|||
# 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)
|
|
@ -127,3 +127,11 @@ class SubcloudShutdownError(PlaybookExecutionFailed):
|
|||
|
||||
class ApiException(DCCommonException):
|
||||
message = _("%(endpoint)s failed with status code: %(rc)d")
|
||||
|
||||
|
||||
class SubcloudNotFound(NotFound):
|
||||
message = _("Subcloud %(subcloud_ref)s not found")
|
||||
|
||||
|
||||
class SubcloudPeerGroupNotFound(NotFound):
|
||||
message = _("Subcloud Peer Group %(peer_group_ref)s not found")
|
||||
|
|
|
@ -0,0 +1,385 @@
|
|||
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import mock
|
||||
import os
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
from dccommon import consts as dccommon_consts
|
||||
from dccommon.drivers.openstack import dcmanager_v1
|
||||
from dccommon import exceptions as dccommon_exceptions
|
||||
from dccommon.tests import base
|
||||
|
||||
FAKE_ID = '1'
|
||||
SUBCLOUD_NAME = 'Subcloud1'
|
||||
SUBCLOUD_BOOTSTRAP_ADDRESS = '192.168.0.10'
|
||||
SUBCLOUD_BOOTSTRAP_VALUE_PATH = '/tmp/test_subcloud_bootstrap_value.yaml'
|
||||
SUBCLOUD_GROUP_NAME = 'SubcloudGroup1'
|
||||
SYSTEM_PEER_UUID = str(uuid.uuid4())
|
||||
SYSTEM_PEER_NAME = 'SystemPeer1'
|
||||
SUBCLOUD_PEER_GROUP_ID = 1
|
||||
SUBCLOUD_PEER_GROUP_NAME = 'SubcloudPeerGroup1'
|
||||
|
||||
FAKE_ENDPOINT = 'http://128.128.1.1:8119/v1.0'
|
||||
FAKE_TOKEN = 'token'
|
||||
FAKE_TIMEOUT = 600
|
||||
|
||||
FAKE_SUBCLOUD_DATA = {"id": FAKE_ID,
|
||||
"name": SUBCLOUD_NAME,
|
||||
"description": "subcloud1 description",
|
||||
"location": "subcloud1 location",
|
||||
"software-version": "22.12",
|
||||
"management-state": "managed",
|
||||
"deploy-status": "complete",
|
||||
"management-subnet": "192.168.101.0/24",
|
||||
"management-start-ip": "192.168.101.2",
|
||||
"management-end-ip": "192.168.101.50",
|
||||
"management-gateway-ip": "192.168.101.1",
|
||||
"systemcontroller-gateway-ip": "192.168.204.101",
|
||||
"group-id": 1,
|
||||
"peer-group-id": SUBCLOUD_PEER_GROUP_ID,
|
||||
"rehome-data": "null",
|
||||
"availability-status": "disabled"}
|
||||
|
||||
FAKE_SUBCLOUD_PEER_GROUP_DATA = {
|
||||
"id": SUBCLOUD_PEER_GROUP_ID,
|
||||
"peer-group-name": SUBCLOUD_PEER_GROUP_NAME,
|
||||
"system-leader-id": SYSTEM_PEER_UUID,
|
||||
"system-leader-name": SYSTEM_PEER_NAME,
|
||||
"max-subcloud-rehoming": 1,
|
||||
"group-state": "enabled",
|
||||
"group-priority": 1
|
||||
}
|
||||
|
||||
|
||||
class TestDcmanagerClient(base.DCCommonTestCase):
|
||||
def setUp(self):
|
||||
super(TestDcmanagerClient, self).setUp()
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud(self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = FAKE_SUBCLOUD_DATA
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
actual_subcloud = client.get_subcloud(SUBCLOUD_NAME)
|
||||
self.assertEqual(SUBCLOUD_NAME, actual_subcloud.get('name'))
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud_not_found(self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 404
|
||||
mock_response.text = "Subcloud not found"
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
self.assertRaises(dccommon_exceptions.SubcloudNotFound,
|
||||
client.get_subcloud, SUBCLOUD_NAME)
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud_list(self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"subclouds": [FAKE_SUBCLOUD_DATA]}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
actual_subclouds = client.get_subcloud_list()
|
||||
self.assertEqual(1, len(actual_subclouds))
|
||||
self.assertEqual(SUBCLOUD_NAME, actual_subclouds[0].get('name'))
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud_group_list(self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"subcloud_groups": [{"name": SUBCLOUD_GROUP_NAME}]}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
actual_subcloud_groups = client.get_subcloud_group_list()
|
||||
self.assertEqual(1, len(actual_subcloud_groups))
|
||||
self.assertEqual(SUBCLOUD_GROUP_NAME,
|
||||
actual_subcloud_groups[0].get('name'))
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud_peer_group_list(self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"subcloud_peer_groups": [FAKE_SUBCLOUD_PEER_GROUP_DATA]}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
actual_peer_group = client.get_subcloud_peer_group_list()
|
||||
self.assertEqual(1, len(actual_peer_group))
|
||||
self.assertEqual(SUBCLOUD_PEER_GROUP_NAME,
|
||||
actual_peer_group[0].get('peer-group-name'))
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud_peer_group(self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = FAKE_SUBCLOUD_PEER_GROUP_DATA
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
actual_peer_group = client.get_subcloud_peer_group(
|
||||
SUBCLOUD_PEER_GROUP_NAME)
|
||||
self.assertEqual(SUBCLOUD_PEER_GROUP_NAME,
|
||||
actual_peer_group.get('peer-group-name'))
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud_peer_group_not_found(
|
||||
self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 404
|
||||
mock_response.text = "Subcloud Peer Group not found"
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
self.assertRaises(dccommon_exceptions.SubcloudPeerGroupNotFound,
|
||||
client.get_subcloud_peer_group,
|
||||
SUBCLOUD_PEER_GROUP_NAME)
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud_list_by_peer_group(self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"subclouds": [FAKE_SUBCLOUD_DATA]}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
actual_subclouds = client.get_subcloud_list_by_peer_group(
|
||||
SUBCLOUD_PEER_GROUP_NAME)
|
||||
self.assertEqual(1, len(actual_subclouds))
|
||||
self.assertEqual(SUBCLOUD_NAME, actual_subclouds[0].get('name'))
|
||||
|
||||
@mock.patch('requests.get')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_get_subcloud_list_by_peer_group_not_found(
|
||||
self, mock_client_init, mock_get):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 404
|
||||
mock_response.text = "Subcloud Peer Group not found"
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
self.assertRaises(dccommon_exceptions.SubcloudPeerGroupNotFound,
|
||||
client.get_subcloud_list_by_peer_group,
|
||||
SUBCLOUD_PEER_GROUP_NAME)
|
||||
|
||||
@mock.patch('requests.post')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_add_subcloud_peer_group(self, mock_client_init, mock_post):
|
||||
peer_group_kwargs = {
|
||||
'peer-group-name': SUBCLOUD_PEER_GROUP_NAME
|
||||
}
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = FAKE_SUBCLOUD_PEER_GROUP_DATA
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
actual_peer_group = client.add_subcloud_peer_group(
|
||||
**peer_group_kwargs)
|
||||
self.assertEqual(SUBCLOUD_PEER_GROUP_NAME,
|
||||
actual_peer_group.get('peer-group-name'))
|
||||
|
||||
@mock.patch('requests.post')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_add_subcloud_with_secondary_status(self, mock_client_init,
|
||||
mock_post):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = FAKE_SUBCLOUD_DATA
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
# create the cache file for subcloud create
|
||||
yaml_data = yaml.dump(FAKE_SUBCLOUD_DATA)
|
||||
with open(SUBCLOUD_BOOTSTRAP_VALUE_PATH, 'w') as file:
|
||||
file.write(yaml_data)
|
||||
|
||||
subcloud_kwargs = {
|
||||
"data": {
|
||||
"bootstrap-address": SUBCLOUD_BOOTSTRAP_ADDRESS
|
||||
},
|
||||
"files": {
|
||||
"bootstrap_values": SUBCLOUD_BOOTSTRAP_VALUE_PATH
|
||||
}
|
||||
}
|
||||
actual_subcloud = client.add_subcloud_with_secondary_status(
|
||||
**subcloud_kwargs)
|
||||
self.assertEqual(SUBCLOUD_NAME, actual_subcloud.get('name'))
|
||||
|
||||
# purge the cache file
|
||||
os.remove(SUBCLOUD_BOOTSTRAP_VALUE_PATH)
|
||||
|
||||
@mock.patch('requests.delete')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_delete_subcloud_peer_group(self, mock_client_init, mock_delete):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = ''
|
||||
mock_delete.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
result = client.delete_subcloud_peer_group(SUBCLOUD_PEER_GROUP_NAME)
|
||||
mock_delete.assert_called_once_with(
|
||||
FAKE_ENDPOINT + '/subcloud-peer-groups/' +
|
||||
SUBCLOUD_PEER_GROUP_NAME,
|
||||
headers={"X-Auth-Token": FAKE_TOKEN},
|
||||
timeout=FAKE_TIMEOUT
|
||||
)
|
||||
self.assertEqual(result, '')
|
||||
|
||||
@mock.patch('requests.delete')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_delete_subcloud_peer_group_not_found(self, mock_client_init,
|
||||
mock_delete):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 404
|
||||
mock_response.text = "Subcloud Peer Group not found"
|
||||
mock_delete.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
self.assertRaises(dccommon_exceptions.SubcloudPeerGroupNotFound,
|
||||
client.delete_subcloud_peer_group,
|
||||
SUBCLOUD_PEER_GROUP_NAME)
|
||||
|
||||
@mock.patch('requests.delete')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_delete_subcloud(self, mock_client_init, mock_delete):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = ''
|
||||
mock_delete.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
result = client.delete_subcloud(SUBCLOUD_NAME)
|
||||
mock_delete.assert_called_once_with(
|
||||
FAKE_ENDPOINT + '/subclouds/' + SUBCLOUD_NAME,
|
||||
headers={"X-Auth-Token": FAKE_TOKEN},
|
||||
timeout=FAKE_TIMEOUT
|
||||
)
|
||||
self.assertEqual(result, '')
|
||||
|
||||
@mock.patch('requests.delete')
|
||||
@mock.patch.object(dcmanager_v1.DcmanagerClient, '__init__')
|
||||
def test_delete_subcloud_not_found(self, mock_client_init,
|
||||
mock_delete):
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.status_code = 404
|
||||
mock_response.text = "Subcloud not found"
|
||||
mock_delete.return_value = mock_response
|
||||
|
||||
mock_client_init.return_value = None
|
||||
client = dcmanager_v1.DcmanagerClient(
|
||||
dccommon_consts.SYSTEM_CONTROLLER_NAME, None)
|
||||
client.endpoint = FAKE_ENDPOINT
|
||||
client.token = FAKE_TOKEN
|
||||
client.timeout = FAKE_TIMEOUT
|
||||
|
||||
self.assertRaises(dccommon_exceptions.SubcloudNotFound,
|
||||
client.delete_subcloud,
|
||||
SUBCLOUD_NAME)
|
Loading…
Reference in New Issue