From 2aedbcda09915c0b21b4fc963901243e184e8035 Mon Sep 17 00:00:00 2001 From: Dan Voiculeasa Date: Mon, 9 Nov 2020 13:21:15 +0200 Subject: [PATCH] Introduce lifecycle operator to platform app A big chunk of logic is moved from sysinv conductor to application itself. Following hooks were necessary: pre-apply-rbd, pre-apply-resource, post-remove-rbd, post-remove-resource, auto-apply-semantic-check, post-armada-request Change-Id: Ibe994411fee55c84fa86770fad5497040f13b78f Story: 2007960 Task: 41292 Signed-off-by: Dan Voiculeasa --- .../k8sapp_platform/lifecycle/__init__.py | 5 + .../lifecycle/lifecycle_platform.py | 123 ++++++++++++++++++ .../k8sapp_platform/setup.cfg | 3 + 3 files changed, 131 insertions(+) create mode 100644 python-k8sapp-platform/k8sapp_platform/k8sapp_platform/lifecycle/__init__.py create mode 100644 python-k8sapp-platform/k8sapp_platform/k8sapp_platform/lifecycle/lifecycle_platform.py diff --git a/python-k8sapp-platform/k8sapp_platform/k8sapp_platform/lifecycle/__init__.py b/python-k8sapp-platform/k8sapp_platform/k8sapp_platform/lifecycle/__init__.py new file mode 100644 index 0000000..6be15e8 --- /dev/null +++ b/python-k8sapp-platform/k8sapp_platform/k8sapp_platform/lifecycle/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/python-k8sapp-platform/k8sapp_platform/k8sapp_platform/lifecycle/lifecycle_platform.py b/python-k8sapp-platform/k8sapp_platform/k8sapp_platform/lifecycle/lifecycle_platform.py new file mode 100644 index 0000000..5c71324 --- /dev/null +++ b/python-k8sapp-platform/k8sapp_platform/k8sapp_platform/lifecycle/lifecycle_platform.py @@ -0,0 +1,123 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# All Rights Reserved. +# + +""" System inventory App lifecycle operator.""" +# Temporary disable pylint for lifecycle hooks until Ic83fbd25d23ae34889cb288330ec448f920bda39 merges +# This will be reverted in a future commit +# pylint: disable=no-member +# pylint: disable=no-name-in-module +import os + +from oslo_log import log as logging +from sysinv.common import constants +from sysinv.common import exception +from sysinv.helm import lifecycle_base as base +from sysinv.helm import lifecycle_utils as lifecycle_utils +from sysinv.helm.lifecycle_constants import LifecycleConstants + +LOG = logging.getLogger(__name__) + + +class PlatformAppLifecycleOperator(base.AppLifecycleOperator): + def app_lifecycle_actions(self, context, conductor_obj, app_op, app, hook_info): + """ Perform lifecycle actions for an operation + + :param context: request context + :param conductor_obj: conductor object + :param app_op: AppOperator object + :param app: AppOperator.Application object + :param hook_info: LifecycleHookInfo object + + """ + # Semantic checks + if hook_info.lifecycle_type == constants.APP_LIFECYCLE_TYPE_SEMANTIC_CHECK: + if hook_info.mode == constants.APP_LIFECYCLE_MODE_AUTO and \ + hook_info.operation == constants.APP_APPLY_OP and \ + hook_info.relative_timing == constants.APP_LIFECYCLE_TIMING_PRE: + return self.pre_auto_apply_check(conductor_obj) + + # Rbd + elif hook_info.lifecycle_type == constants.APP_LIFECYCLE_TYPE_RBD: + if hook_info.operation == constants.APP_APPLY_OP and \ + hook_info.relative_timing == constants.APP_LIFECYCLE_TIMING_PRE: + return lifecycle_utils.create_rbd_provisioner_secrets(app_op, app, hook_info) + elif hook_info.operation == constants.APP_REMOVE_OP and \ + hook_info.relative_timing == constants.APP_LIFECYCLE_TIMING_POST: + return lifecycle_utils.delete_rbd_provisioner_secrets(app_op, app, hook_info) + + # Resources + elif hook_info.lifecycle_type == constants.APP_LIFECYCLE_TYPE_RESOURCE: + if hook_info.operation == constants.APP_APPLY_OP and \ + hook_info.relative_timing == constants.APP_LIFECYCLE_TIMING_PRE: + return lifecycle_utils.create_local_registry_secrets(app_op, app, hook_info) + elif hook_info.operation == constants.APP_REMOVE_OP and \ + hook_info.relative_timing == constants.APP_LIFECYCLE_TIMING_POST: + return lifecycle_utils.delete_local_registry_secrets(app_op, app, hook_info) + + # Armada apply retry + elif hook_info.lifecycle_type == constants.APP_LIFECYCLE_TYPE_ARMADA_REQUEST: + if hook_info.operation == constants.APP_APPLY_OP and \ + hook_info.relative_timing == constants.APP_LIFECYCLE_TIMING_POST: + return self.armada_apply_retry(app_op, app, hook_info) + + # Use the default behaviour for other hooks + super(PlatformAppLifecycleOperator, self).app_lifecycle_actions(context, conductor_obj, app_op, app, hook_info) + + def pre_auto_apply_check(self, conductor_obj): + """ Semantic check for auto-apply + + Check: + - ceph access + - ceph health + - crushmap applied + - replica count is non-zero so that manifest apply will not timeout + + :param conductor_obj: conductor object + + """ + crushmap_flag_file = os.path.join(constants.SYSINV_CONFIG_PATH, + constants.CEPH_CRUSH_MAP_APPLIED) + + if not os.path.isfile(crushmap_flag_file): + raise exception.LifecycleSemanticCheckException( + "Crush map not applied") + if not conductor_obj._ceph.have_ceph_monitor_access(): + raise exception.LifecycleSemanticCheckException( + "Monitor access error") + if not conductor_obj._ceph.ceph_status_ok(): + raise exception.LifecycleSemanticCheckException( + "Ceph status is not HEALTH_OK") + if conductor_obj.dbapi.count_hosts_matching_criteria( + personality=constants.CONTROLLER, + administrative=constants.ADMIN_UNLOCKED, + operational=constants.OPERATIONAL_ENABLED, + availability=[constants.AVAILABILITY_AVAILABLE, + constants.AVAILABILITY_DEGRADED], + vim_progress_status=constants.VIM_SERVICES_ENABLED) < 1: + raise exception.LifecycleSemanticCheckException( + "Not enough hosts in desired state") + + def armada_apply_retry(self, app_op, app, hook_info): + """Retry armada apply + + :param app_op: AppOperator object + :param app: AppOperator.Application object + :param hook_info: LifecycleHookInfo object + """ + if LifecycleConstants.EXTRA not in hook_info: + raise exception.LifecycleMissingInfo("Missing {}".format(LifecycleConstants.EXTRA)) + if LifecycleConstants.RETURN_CODE not in hook_info[LifecycleConstants.EXTRA]: + raise exception.LifecycleMissingInfo( + "Missing {} {}".format(LifecycleConstants.EXTRA, LifecycleConstants.RETURN_CODE)) + + # Raise a specific exception to be caught by the + # retry decorator and attempt a re-apply + if not hook_info[LifecycleConstants.EXTRA][LifecycleConstants.RETURN_CODE] and \ + not app_op.is_app_aborted(app.name): + LOG.info("%s app failed applying. Retrying." % str(app.name)) + raise exception.ApplicationApplyFailure(name=app.name) diff --git a/python-k8sapp-platform/k8sapp_platform/setup.cfg b/python-k8sapp-platform/k8sapp_platform/setup.cfg index 95ced37..bd99fa2 100644 --- a/python-k8sapp-platform/k8sapp_platform/setup.cfg +++ b/python-k8sapp-platform/k8sapp_platform/setup.cfg @@ -41,5 +41,8 @@ systemconfig.helm_plugins.platform_integ_apps = systemconfig.armada.manifest_ops = platform-integ-apps = k8sapp_platform.armada.manifest_platform:PlatformArmadaManifestOperator +systemconfig.app_lifecycle = + platform-integ-apps = k8sapp_platform.lifecycle.lifecycle_platform:PlatformAppLifecycleOperator + [wheel] universal = 1