From 491cca42ed854d2cb3ee3646b93c56a4f45f563c Mon Sep 17 00:00:00 2001 From: Elena Taivan Date: Wed, 29 Apr 2020 11:25:26 +0000 Subject: [PATCH] Qcow2 conversion to raw can be done using 'image-conversion' filesystem 1. Conversion filesystem can be added before/after stx-openstack is applied 2. If conversion filesystem is added after stx-openstack is applied, changes to stx-openstack will only take effect once the application is re-applied 3. It is not allowed to delete image-conversion filesystem when stx-openstack is in applying/applied/removing state 4. Raise alarms for image-conversion Change-Id: Ie205329b694525509b0820497186fcd9ec2e45c9 Closes-bug: 1819688 Depends-On: https://review.opendev.org/#/c/724270/ Depends-On: https://review.opendev.org/724288/ Signed-off-by: Elena Taivan --- .../sysinv/api/controllers/v1/host_fs.py | 12 +++- .../sysinv/sysinv/sysinv/conductor/manager.py | 71 +++++++++++++++++++ sysinv/sysinv/sysinv/sysinv/helm/cinder.py | 34 ++++++++- .../sysinv/sysinv/tests/helm/test_cinder.py | 30 ++++++++ tsconfig/tsconfig/tsconfig/tsconfig.py | 1 + 5 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 sysinv/sysinv/sysinv/sysinv/tests/helm/test_cinder.py diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host_fs.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host_fs.py index d839c9b249..a2c9a669ee 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host_fs.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host_fs.py @@ -472,8 +472,18 @@ def _delete(host_fs): _check_host_fs(host_fs) - ihost = pecan.request.dbapi.ihost_get(host_fs['forihostid']) + if host_fs['name'] == constants.FILESYSTEM_NAME_IMAGE_CONVERSION: + try: + app = pecan.request.dbapi.kube_app_get(constants.HELM_APP_OPENSTACK) + if app.status != constants.APP_UPLOAD_SUCCESS: + raise wsme.exc.ClientSideError(_("Deleting filesystem %s is not allowed " + "when stx-openstack is in %s state" % + (host_fs['name'], app.status))) + except exception.KubeAppNotFound: + LOG.info("Application %s not found, deleting %s fs" % + constants.HELM_APP_OPENSTACK, host_fs['name']) + ihost = pecan.request.dbapi.ihost_get(host_fs['forihostid']) try: pecan.request.dbapi.host_fs_destroy(host_fs['id']) except exception.HTTPNotFound: diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index 8575f2e9f4..2440e45e08 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -4946,6 +4946,9 @@ class ConductorManager(service.PeriodicService): # Audit kubernetes node labels self._audit_kubernetes_labels(hosts) + # Audit image conversion + self._audit_image_conversion(hosts) + for host in hosts: # only audit configured hosts if not host.personality: @@ -5015,6 +5018,49 @@ class ConductorManager(service.PeriodicService): elif bk.backend in self._stor_bck_op_timeouts: del self._stor_bck_op_timeouts[bk.backend] + def _audit_image_conversion(self, hosts): + """ + Raise alarm if: + - image-conversion is not added on both controllers; + - the size of the filesystem is not the same + on both controllers + """ + chosts = [h for h in hosts if h.personality == constants.CONTROLLER] + if len(chosts) <= 1: + # No alarm is raised if setup has only one controller + return + + conversion_list = [] + for host in chosts: + hostfs_list = self.dbapi.host_fs_get_by_ihost(host.uuid) + for host_fs in hostfs_list: + if host_fs['name'] == constants.FILESYSTEM_NAME_IMAGE_CONVERSION: + conversion_list.append(host_fs['size']) + + reason_text = "image-conversion must be added on both controllers" + if not conversion_list: + # If no conversion filesystem is present on any host + # any alarm present is cleared + self._update_image_conversion_alarm(fm_constants.FM_ALARM_STATE_CLEAR, + constants.FILESYSTEM_NAME_IMAGE_CONVERSION) + elif (len(conversion_list) == 1): + self._update_image_conversion_alarm(fm_constants.FM_ALARM_STATE_SET, + constants.FILESYSTEM_NAME_IMAGE_CONVERSION, + reason_text) + else: + # If conversion filesystem is present on both controllers + # with different sizes + self._update_image_conversion_alarm(fm_constants.FM_ALARM_STATE_CLEAR, + constants.FILESYSTEM_NAME_IMAGE_CONVERSION) + if (conversion_list[0] != conversion_list[1]): + reason_text = "image-conversion size must be the same on both controllers" + self._update_image_conversion_alarm(fm_constants.FM_ALARM_STATE_SET, + constants.FILESYSTEM_NAME_IMAGE_CONVERSION, + reason_text) + elif conversion_list[0] == conversion_list[1]: + self._update_image_conversion_alarm(fm_constants.FM_ALARM_STATE_CLEAR, + constants.FILESYSTEM_NAME_IMAGE_CONVERSION) + def _auto_upload_managed_app(self, context, app_name): if self._patching_operation_is_occurring(): return @@ -6430,6 +6476,31 @@ class ConductorManager(service.PeriodicService): 'task': None} self.dbapi.storage_ceph_external_update(sb_uuid, values) + def _update_image_conversion_alarm(self, alarm_state, fs_name, reason_text=None): + """ Raise conversion configuration alarm""" + entity_instance_id = "%s=%s" % (fm_constants.FM_ENTITY_TYPE_IMAGE_CONVERSION, + fs_name) + + if alarm_state == fm_constants.FM_ALARM_STATE_SET: + fault = fm_api.Fault( + alarm_id=fm_constants.FM_ALARM_ID_IMAGE_CONVERSION, + alarm_state=alarm_state, + entity_type_id=fm_constants.FM_ENTITY_TYPE_IMAGE_CONVERSION, + entity_instance_id=entity_instance_id, + severity=fm_constants.FM_ALARM_SEVERITY_CRITICAL, + reason_text=reason_text, + alarm_type=fm_constants.FM_ALARM_TYPE_4, + probable_cause=fm_constants.ALARM_PROBABLE_CAUSE_7, + proposed_repair_action=_("Add image-conversion filesystem on both controllers." + "Consult the System Administration Manual " + "for more details. If problem persists, " + "contact next level of support."), + service_affecting=True) + self.fm_api.set_fault(fault) + else: + self.fm_api.clear_fault(fm_constants.FM_ALARM_ID_IMAGE_CONVERSION, + entity_instance_id) + def _update_storage_backend_alarm(self, alarm_state, backend, reason_text=None): """ Update storage backend configuration alarm""" entity_instance_id = "%s=%s" % (fm_constants.FM_ENTITY_TYPE_STORAGE_BACKEND, diff --git a/sysinv/sysinv/sysinv/sysinv/helm/cinder.py b/sysinv/sysinv/sysinv/sysinv/helm/cinder.py index b47ad73e06..2fe984ed57 100644 --- a/sysinv/sysinv/sysinv/sysinv/helm/cinder.py +++ b/sysinv/sysinv/sysinv/sysinv/helm/cinder.py @@ -1,9 +1,10 @@ # -# Copyright (c) 2018-2019 Wind River Systems, Inc. +# Copyright (c) 2018-2020 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # +import tsconfig.tsconfig as tsc from sysinv.common import constants from sysinv.common import exception from sysinv.common.storage_backend_conf import StorageBackendConfig @@ -21,10 +22,30 @@ class CinderHelm(openstack.OpenstackBaseHelm): SERVICE_TYPE = 'volume' AUTH_USERS = ['cinder'] + def _get_mount_overrides(self): + overrides = { + 'volumes': [], + 'volumeMounts': [] + } + overrides['volumes'].append({ + 'name': 'newvolume', + 'hostPath': {'path': tsc.IMAGE_CONVERSION_PATH} + }) + overrides['volumeMounts'].append({ + 'name': 'newvolume', + 'mountPath': tsc.IMAGE_CONVERSION_PATH + }) + return overrides + def get_overrides(self, namespace=None): overrides = { common.HELM_NS_OPENSTACK: { 'pod': { + 'mounts': { + 'cinder_volume': { + 'cinder_volume': self._get_mount_overrides() + } + }, 'replicas': { 'api': self._num_controllers(), 'volume': self._num_controllers(), @@ -99,6 +120,17 @@ class CinderHelm(openstack.OpenstackBaseHelm): str(b.name.encode('utf8', 'strict').decode('utf-8')) for b in backends) }, } + current_host_fs_list = self.dbapi.host_fs_get_list() + + chosts = self.dbapi.ihost_get_by_personality(constants.CONTROLLER) + chosts_fs = [fs for fs in current_host_fs_list + if fs['name'] == constants.FILESYSTEM_NAME_IMAGE_CONVERSION] + + # conversion overrides should be generated only if each controller node + # configured has the conversion partition added + if len(chosts) == len(chosts_fs): + conf_cinder['DEFAULT']['image_conversion_dir'] = \ + tsc.IMAGE_CONVERSION_PATH # Always set the default_volume_type to the volume type associated with the # primary Ceph backend/tier which is available on all StarlingX platform diff --git a/sysinv/sysinv/sysinv/sysinv/tests/helm/test_cinder.py b/sysinv/sysinv/sysinv/sysinv/tests/helm/test_cinder.py new file mode 100644 index 0000000000..0494deaf6d --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/tests/helm/test_cinder.py @@ -0,0 +1,30 @@ +import tsconfig.tsconfig as tsc +from sysinv.helm import common + +from sysinv.tests.db import base as dbbase +from sysinv.tests.db import utils as dbutils +from sysinv.tests.helm import base +from sysinv.tests.helm import test_helm + + +class CinderConversionTestCase(test_helm.StxOpenstackAppMixin, + base.HelmTestCaseMixin): + def setUp(self): + super(CinderConversionTestCase, self).setUp() + self.app = dbutils.create_test_app(name=self.app_name) + + +class CinderGetOverrideTest(CinderConversionTestCase, + dbbase.ControllerHostTestCase): + def test_cinder_overrides(self): + dbutils.create_test_host_fs(name='image-conversion', + forihostid=self.host.id) + overrides = self.operator.get_helm_chart_overrides( + common.HELM_CHART_CINDER, + cnamespace=common.HELM_NS_OPENSTACK) + self.assertOverridesParameters(overrides, { + 'conf': { + 'cinder': { + 'DEFAULT': { + 'image_conversion_dir': tsc.IMAGE_CONVERSION_PATH}}} + }) diff --git a/tsconfig/tsconfig/tsconfig/tsconfig.py b/tsconfig/tsconfig/tsconfig/tsconfig.py index 50ddf5f9a2..0486842459 100644 --- a/tsconfig/tsconfig/tsconfig/tsconfig.py +++ b/tsconfig/tsconfig/tsconfig/tsconfig.py @@ -173,6 +173,7 @@ KEYRING_PATH = PLATFORM_PATH + "/.keyring/" + SW_VERSION DEPLOY_PATH = PLATFORM_PATH + "/deploy/" + SW_VERSION ETCD_PATH = "/opt/etcd" EXTENSION_PATH = "/opt/extension" +IMAGE_CONVERSION_PATH = "/opt/conversion" PLATFORM_CEPH_CONF_PATH = CONFIG_PATH + 'ceph-config' PLATFORM_BACKUP_PATH = '/opt/platform-backup'