From 9231c6765a0b2953314b61d0ecca3fe17d671e31 Mon Sep 17 00:00:00 2001 From: Daniel Badea Date: Tue, 25 Sep 2018 16:09:52 +0000 Subject: [PATCH] protect host capabilities Reject updating read-only host capabilities: 1. stor_function. This field is set to 'monitor' for hosts that are running ceph monitor process: controller-0, controller-1, storage-0. 2. Personality. This field is "virtual": not saved in the database but returned via API and displayed via "system host-show". Change-Id: I6d169b68bffd74e7fae56f9d92d5930593cefe59 Closes-Bug: #1794117 Signed-off-by: Daniel Badea --- .../sysinv/sysinv/api/controllers/v1/host.py | 60 +++++++++++++++++++ .../sysinv/sysinv/sysinv/conductor/manager.py | 23 ++++--- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py index a877764721..2c0e3c09d2 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py @@ -19,6 +19,7 @@ # Copyright (c) 2013-2018 Wind River Systems, Inc. # +import ast import cgi import copy import json @@ -1721,6 +1722,62 @@ class HostController(rest.RestController): def _patch_gen(self, uuid, patch, profile_uuid): return self._patch(uuid, patch, profile_uuid) + @staticmethod + def _validate_capability_is_not_set(old, new): + is_set, _ = new + return not is_set + + @staticmethod + def _validate_capability_is_equal(old, new): + return old == new + + def _validate_capabilities(self, old_caps, new_caps): + """ Reject updating read-only host capabilities: + 1. stor_function. This field is set to 'monitor' for hosts that are + running ceph monitor process: controller-0, controller-1, storage-0. + 2. Personality. This field is "virtual": not saved in the database but + returned via API and displayed via "system host-show". + + :param old_caps: current host capabilities + :type old_caps: dict + :param new_caps: updated host capabilies (to be set) + :type new_caps: str + :raises: wsme.exc.ClientSideError when attempting to change read-only + capabilities + """ + if type(new_caps) == str: + try: + new_caps = ast.literal_eval(new_caps) + except SyntaxError: + pass + if type(new_caps) != dict: + raise wsme.exc.ClientSideError( + _("Changing capabilities type is not allowed: " + "old_value={}, new_value={}").format( + old_caps, new_caps)) + PROTECTED_CAPABILITIES = [ + ('Personality', + self._validate_capability_is_not_set), + (constants.IHOST_STOR_FUNCTION, + self._validate_capability_is_equal)] + for capability, validate in PROTECTED_CAPABILITIES: + old_is_set, old_value = ( + capability in old_caps, old_caps.get(capability)) + new_is_set, new_value = ( + capability in new_caps, new_caps.get(capability)) + if not validate((old_is_set, old_value), + (new_is_set, new_value)): + if old_is_set: + raise wsme.exc.ClientSideError( + _("Changing capability not allowed: " + "name={}, old_value={}, new_value={}. ").format( + capability, old_value, new_value)) + else: + raise wsme.exc.ClientSideError( + _("Setting capability not allowed: " + "name={}, value={}. ").format( + capability, new_value)) + def _patch(self, uuid, patch, myprofile_uuid): log_start = cutils.timestamped("ihost_patch_start") @@ -1745,6 +1802,9 @@ class HostController(rest.RestController): LOG.exception(e) raise wsme.exc.ClientSideError(_("Patching Error: %s") % e) + self._validate_capabilities( + ihost_dict['capabilities'], patched_ihost['capabilities']) + defaults = objects.host.get_defaults() ihost_dict_orig = dict(ihost_obj.as_dict()) diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index 432c03ec11..a4f223949c 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -3096,11 +3096,11 @@ class ConductorManager(service.PeriodicService): self.dbapi, constants.CINDER_BACKEND_CEPH ): - ihost_capabilities = ihost.capabilities - ihost_dict = {'stor_function': constants.STOR_FUNCTION_MONITOR} - ihost_capabilities.update(ihost_dict) - ihost_val = {'capabilities': ihost_capabilities} - self.dbapi.ihost_update(ihost_uuid, ihost_val) + ihost.capabilities.update({ + constants.IHOST_STOR_FUNCTION: + constants.STOR_FUNCTION_MONITOR}) + self.dbapi.ihost_update(ihost_uuid, + {'capabilities': ihost.capabilities}) # Check whether a disk has been removed. if idisks and len(idisk_dict_array) > 0: @@ -4240,14 +4240,11 @@ class ConductorManager(service.PeriodicService): if ceph_backend and ceph_backend.task != \ constants.SB_TASK_PROVISION_STORAGE: - LOG.debug("iplatform monitor check system has ceph backend") - ihost_capabilities = ihost.capabilities - ihost_dict = { - 'stor_function': constants.STOR_FUNCTION_MONITOR, - } - ihost_capabilities.update(ihost_dict) - ihost_val = {'capabilities': ihost_capabilities} - self.dbapi.ihost_update(ihost_uuid, ihost_val) + ihost.capabilities.update({ + constants.IHOST_STOR_FUNCTION: + constants.STOR_FUNCTION_MONITOR}) + self.dbapi.ihost_update(ihost_uuid, + {'capabilities': ihost.capabilities}) storage_lvm = StorageBackendConfig.get_configured_backend_conf( self.dbapi,