From 28aa599715c8bc047af751003f3aa18c5923effc Mon Sep 17 00:00:00 2001 From: Andy Ning Date: Wed, 17 Jul 2019 10:21:16 -0400 Subject: [PATCH] dcmanager for containerized openstack services - service This update enhanced dcmanager to be able to manage containerized openstack services of subclouds that have openstack application deployed. dcmanager audit query subcloud periodically to check whether stx-openstack application is installed or not. If it's installed from previous uninstalled, the subcloud's "openstack_installed" field will be updated from "False" to "True", openstack sync endpoint types will be added into subcloud sync endpoint type list, and notify dcorch to update its sync threads. If it's uninstalled from previous installed, the subcloud's "openstack_installed" field will be updated from "True" back to "False", openstack sync endpoint types will be removed from subcloud sync endpoint type list, and notify dcorch to update its sync threads. The endpoint type sync status of openstack services will be available to dcmanager commands such as "dcmanager subcloud list". Change-Id: I651bebbef0f0a4dd39ec2371a7397c066c747622 Story: 2004766 Task: 36261 Signed-off-by: Andy Ning --- dcmanager/cmd/api.py | 3 +- dcmanager/db/api.py | 16 +++-- dcmanager/db/sqlalchemy/api.py | 16 ++++- .../004_add_openstack_installed_column.py | 38 +++++++++++ dcmanager/db/sqlalchemy/models.py | 1 + dcmanager/drivers/openstack/sysinv_v1.py | 6 ++ dcmanager/manager/subcloud_audit_manager.py | 63 ++++++++++++++++++- dcmanager/manager/subcloud_manager.py | 3 +- .../tests/unit/db/test_subcloud_db_api.py | 2 + 9 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 dcmanager/db/sqlalchemy/migrate_repo/versions/004_add_openstack_installed_column.py diff --git a/dcmanager/cmd/api.py b/dcmanager/cmd/api.py index 2f3bba76f..d62912fa3 100644 --- a/dcmanager/cmd/api.py +++ b/dcmanager/cmd/api.py @@ -27,6 +27,8 @@ import sys import eventlet +eventlet.monkey_patch(os=False) + from oslo_config import cfg from oslo_log import log as logging from oslo_service import systemd @@ -43,7 +45,6 @@ from dcorch.common import messaging as dcorch_messaging CONF = cfg.CONF config.register_options() LOG = logging.getLogger('dcmanager.api') -eventlet.monkey_patch(os=False) def main(): diff --git a/dcmanager/db/api.py b/dcmanager/db/api.py index f8804e679..87b35d7ad 100644 --- a/dcmanager/db/api.py +++ b/dcmanager/db/api.py @@ -63,6 +63,7 @@ def subcloud_db_model_to_dict(subcloud): "management-start-ip": subcloud.management_start_ip, "management-end-ip": subcloud.management_end_ip, "management-gateway-ip": subcloud.management_gateway_ip, + "openstack-installed": subcloud.openstack_installed, "systemcontroller-gateway-ip": subcloud.systemcontroller_gateway_ip, "created-at": subcloud.created_at, @@ -73,13 +74,15 @@ def subcloud_db_model_to_dict(subcloud): def subcloud_create(context, name, description, location, software_version, management_subnet, management_gateway_ip, management_start_ip, management_end_ip, - systemcontroller_gateway_ip, deploy_status): + systemcontroller_gateway_ip, deploy_status, + openstack_installed): """Create a subcloud.""" return IMPL.subcloud_create(context, name, description, location, software_version, management_subnet, management_gateway_ip, management_start_ip, management_end_ip, - systemcontroller_gateway_ip, deploy_status) + systemcontroller_gateway_ip, deploy_status, + openstack_installed) def subcloud_get(context, subcloud_id): @@ -110,12 +113,12 @@ def subcloud_get_all_with_status(context): def subcloud_update(context, subcloud_id, management_state=None, availability_status=None, software_version=None, description=None, location=None, audit_fail_count=None, - deploy_status=None): + deploy_status=None, openstack_installed=None): """Update a subcloud or raise if it does not exist.""" return IMPL.subcloud_update(context, subcloud_id, management_state, availability_status, software_version, description, location, audit_fail_count, - deploy_status) + deploy_status, openstack_installed) def subcloud_destroy(context, subcloud_id): @@ -130,6 +133,11 @@ def subcloud_status_create(context, subcloud_id, endpoint_type): return IMPL.subcloud_status_create(context, subcloud_id, endpoint_type) +def subcloud_status_delete(context, subcloud_id, endpoint_type): + """Delete a subcloud status for an endpoint_type.""" + return IMPL.subcloud_status_delete(context, subcloud_id, endpoint_type) + + def subcloud_status_db_model_to_dict(subcloud_status): """Convert subcloud status db model to dictionary.""" if subcloud_status: diff --git a/dcmanager/db/sqlalchemy/api.py b/dcmanager/db/sqlalchemy/api.py index 36c54d9b6..25bd419f0 100644 --- a/dcmanager/db/sqlalchemy/api.py +++ b/dcmanager/db/sqlalchemy/api.py @@ -205,7 +205,8 @@ def subcloud_get_all_with_status(context): def subcloud_create(context, name, description, location, software_version, management_subnet, management_gateway_ip, management_start_ip, management_end_ip, - systemcontroller_gateway_ip, deploy_status): + systemcontroller_gateway_ip, deploy_status, + openstack_installed): with write_session() as session: subcloud_ref = models.Subcloud() subcloud_ref.name = name @@ -221,6 +222,7 @@ def subcloud_create(context, name, description, location, software_version, subcloud_ref.systemcontroller_gateway_ip = systemcontroller_gateway_ip subcloud_ref.deploy_status = deploy_status subcloud_ref.audit_fail_count = 0 + subcloud_ref.openstack_installed = openstack_installed session.add(subcloud_ref) return subcloud_ref @@ -229,7 +231,7 @@ def subcloud_create(context, name, description, location, software_version, def subcloud_update(context, subcloud_id, management_state=None, availability_status=None, software_version=None, description=None, location=None, audit_fail_count=None, - deploy_status=None): + deploy_status=None, openstack_installed=None): with write_session() as session: subcloud_ref = subcloud_get(context, subcloud_id) if management_state is not None: @@ -246,6 +248,8 @@ def subcloud_update(context, subcloud_id, management_state=None, subcloud_ref.audit_fail_count = audit_fail_count if deploy_status is not None: subcloud_ref.deploy_status = deploy_status + if openstack_installed is not None: + subcloud_ref.openstack_installed = openstack_installed subcloud_ref.save(session) return subcloud_ref @@ -304,6 +308,14 @@ def subcloud_status_create(context, subcloud_id, endpoint_type): return subcloud_status_ref +@require_admin_context +def subcloud_status_delete(context, subcloud_id, endpoint_type): + with write_session() as session: + subcloud_status_ref = subcloud_status_get(context, subcloud_id, + endpoint_type) + session.delete(subcloud_status_ref) + + @require_admin_context def subcloud_status_update(context, subcloud_id, endpoint_type, sync_status): with write_session() as session: diff --git a/dcmanager/db/sqlalchemy/migrate_repo/versions/004_add_openstack_installed_column.py b/dcmanager/db/sqlalchemy/migrate_repo/versions/004_add_openstack_installed_column.py new file mode 100644 index 000000000..65f2ce181 --- /dev/null +++ b/dcmanager/db/sqlalchemy/migrate_repo/versions/004_add_openstack_installed_column.py @@ -0,0 +1,38 @@ +# 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. +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# The right to copy, distribute, modify, or otherwise make use +# of this software may be licensed only pursuant to the terms +# of an applicable Wind River license agreement. +# + +from sqlalchemy import Column, MetaData, Table, Boolean + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + subclouds = Table('subclouds', meta, autoload=True) + + # Add the 'openstack_installed' column to the subclouds table. + subclouds.create_column(Column('openstack_installed', Boolean, + nullable=False, default=False, + server_default='0')) + + return True + + +def downgrade(migrate_engine): + raise NotImplementedError('Database downgrade is unsupported.') diff --git a/dcmanager/db/sqlalchemy/models.py b/dcmanager/db/sqlalchemy/models.py index 5b3538bd2..a3cc6eb74 100644 --- a/dcmanager/db/sqlalchemy/models.py +++ b/dcmanager/db/sqlalchemy/models.py @@ -92,6 +92,7 @@ class Subcloud(BASE, DCManagerBase): management_gateway_ip = Column(String(255)) management_start_ip = Column(String(255), unique=True) management_end_ip = Column(String(255), unique=True) + openstack_installed = Column(Boolean, nullable=False, default=False) systemcontroller_gateway_ip = Column(String(255)) audit_fail_count = Column(Integer) diff --git a/dcmanager/drivers/openstack/sysinv_v1.py b/dcmanager/drivers/openstack/sysinv_v1.py index faddc5cb5..e84824e56 100644 --- a/dcmanager/drivers/openstack/sysinv_v1.py +++ b/dcmanager/drivers/openstack/sysinv_v1.py @@ -124,3 +124,9 @@ class SysinvClient(base.DriverBase): def get_loads(self): """Get a list of loads.""" return self.sysinv_client.load.list() + + def get_applications(self): + """Get a list of containerized applications""" + + # Get a list of containerized applications the system knows of + return self.sysinv_client.app.list() diff --git a/dcmanager/manager/subcloud_audit_manager.py b/dcmanager/manager/subcloud_audit_manager.py index 8e0af1a0c..0bc4eb1ae 100644 --- a/dcmanager/manager/subcloud_audit_manager.py +++ b/dcmanager/manager/subcloud_audit_manager.py @@ -22,7 +22,7 @@ from oslo_log import log as logging - +from dcorch.common import consts as dcorch_consts from dcorch.drivers.openstack.keystone_v3 import KeystoneClient from dcorch.rpc import client as dcorch_rpc_client @@ -38,6 +38,7 @@ from keystoneauth1 import exceptions as keystone_exceptions from fm_api import constants as fm_const from fm_api import fm_api +from sysinv.common import constants as sysinv_constants LOG = logging.getLogger(__name__) @@ -79,6 +80,7 @@ class SubcloudAuditManager(manager.Manager): management_state = subcloud.management_state avail_status_current = subcloud.availability_status audit_fail_count = subcloud.audit_fail_count + openstack_installed = subcloud.openstack_installed # Set defaults to None and disabled so we will still set disabled # status if we encounter an error. @@ -106,6 +108,7 @@ class SubcloudAuditManager(manager.Manager): LOG.exception(e) if sysinv_client: + # get a list of service groups in the subcloud try: svc_groups = sysinv_client.get_service_groups() except Exception as e: @@ -257,3 +260,61 @@ class SubcloudAuditManager(manager.Manager): LOG.info('Ignoring SubcloudNotFound when attempting ' 'audit_fail_count update: %s' % subcloud_name) continue + + if sysinv_client: + # get a list of installed apps in the subcloud + try: + apps = sysinv_client.get_applications() + except Exception as e: + apps = None + LOG.warn('Cannot retrieve installed apps for ' + 'subcloud:%s, %s' % (subcloud_name, e)) + + if apps: + openstack_installed_current = False + for app in apps: + if app.name == sysinv_constants.HELM_APP_OPENSTACK\ + and app.active: + # audit find openstack app is installed and active in + # the subcloud + openstack_installed_current = True + break + + dcm_update_func = None + dco_update_func = None + if openstack_installed_current and not openstack_installed: + dcm_update_func = db_api.subcloud_status_create + dco_update_func = self.dcorch_rpc_client.\ + add_subcloud_sync_endpoint_type + elif not openstack_installed_current and openstack_installed: + dcm_update_func = db_api.subcloud_status_delete + dco_update_func = self.dcorch_rpc_client.\ + remove_subcloud_sync_endpoint_type + + if dcm_update_func and dco_update_func: + endpoint_type_list = dcorch_consts.ENDPOINT_TYPES_LIST_OS + try: + # Notify dcorch to add/remove sync endpoint type list + dco_update_func(self.context, subcloud_name, + endpoint_type_list) + LOG.info('Notifying dcorch, subcloud: %s new sync' + ' endpoint: %s' % (subcloud_name, + endpoint_type_list)) + # Update subcloud status table by adding/removing + # openstack sync endpoint types. + for endpoint_type in endpoint_type_list: + dcm_update_func(self.context, subcloud_id, + endpoint_type) + # Update openstack_installed of subcloud table + db_api.subcloud_update( + self.context, subcloud_id, + openstack_installed=openstack_installed_current) + except exceptions.SubcloudNotFound: + LOG.info('Ignoring SubcloudNotFound when attempting' + ' openstack_installed update: %s' + % subcloud_name) + except Exception as e: + LOG.exception(e) + LOG.warn('Problem informing dcorch of subcloud ' + 'sync endpoint type change, subcloud: %s' + % subcloud_name) diff --git a/dcmanager/manager/subcloud_manager.py b/dcmanager/manager/subcloud_manager.py index 7fd9ca163..29f27309b 100644 --- a/dcmanager/manager/subcloud_manager.py +++ b/dcmanager/manager/subcloud_manager.py @@ -120,7 +120,8 @@ class SubcloudManager(manager.Manager): payload['management_start_address'], payload['management_end_address'], payload['systemcontroller_gateway_address'], - consts.DEPLOY_STATE_NONE) + consts.DEPLOY_STATE_NONE, + False) except Exception as e: LOG.exception(e) raise e diff --git a/dcmanager/tests/unit/db/test_subcloud_db_api.py b/dcmanager/tests/unit/db/test_subcloud_db_api.py index 9a6e6ebc4..988ee9301 100644 --- a/dcmanager/tests/unit/db/test_subcloud_db_api.py +++ b/dcmanager/tests/unit/db/test_subcloud_db_api.py @@ -86,6 +86,7 @@ class DBAPISubcloudTest(base.DCManagerTestCase): 'management_end_ip': "192.168.101.50", 'systemcontroller_gateway_ip': "192.168.204.101", 'deploy_status': "not-deployed", + 'openstack_installed': False, } values.update(kwargs) return db_api.subcloud_create(ctxt, **values) @@ -104,6 +105,7 @@ class DBAPISubcloudTest(base.DCManagerTestCase): 'systemcontroller_gateway_ip': data[ 'systemcontroller_gateway_address'], 'deploy_status': "not-deployed", + 'openstack_installed': False, } return db_api.subcloud_create(ctxt, **values)