diff --git a/puppet-manifests/src/modules/platform/manifests/ceph.pp b/puppet-manifests/src/modules/platform/manifests/ceph.pp index 524129d24a..d773991f1e 100644 --- a/puppet-manifests/src/modules/platform/manifests/ceph.pp +++ b/puppet-manifests/src/modules/platform/manifests/ceph.pp @@ -439,7 +439,7 @@ class platform::ceph::rgw::keystone::auth( } -class platform::ceph::controller::runtime { +class platform::ceph::runtime { include ::platform::ceph::monitor include ::platform::ceph @@ -452,7 +452,3 @@ class platform::ceph::controller::runtime { } } } - -class platform::ceph::compute::runtime { - include ::platform::ceph -} diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/ceph_mon.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/ceph_mon.py index 52bdc37cbe..31157f2028 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/ceph_mon.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/ceph_mon.py @@ -10,8 +10,7 @@ from cgtsclient.common import base from cgtsclient import exc -CREATION_ATTRIBUTES = ['ceph_mon_gib', 'ceph_mon_dev', - 'ceph_mon_dev_ctrl0', 'ceph_mon_dev_ctrl1'] +CREATION_ATTRIBUTES = ['ihost_uuid'] class CephMon(base.Resource): @@ -36,8 +35,8 @@ class CephMonManager(base.Manager): path = '/v1/ceph_mon' return self._list(path, "ceph_mon") - def get(self, ceph_mon_id): - path = '/v1/ceph_mon/%s' % ceph_mon_id + def get(self, ceph_mon_uuid): + path = '/v1/ceph_mon/%s' % ceph_mon_uuid try: return self._list(path)[0] except IndexError: @@ -53,6 +52,10 @@ class CephMonManager(base.Manager): raise exc.InvalidAttribute('%s' % key) return self._create(path, new) + def delete(self, host_uuid): + path = '/v1/ceph_mon/%s' % host_uuid + return self._delete(path) + def update(self, ceph_mon_id, patch): path = '/v1/ceph_mon/%s' % ceph_mon_id return self._update(path, patch) @@ -62,7 +65,7 @@ class CephMonManager(base.Manager): return self._json_get(path, {}) -def ceph_mon_add(cc, args): +def ceph_mon_add(cc, args, ihost_uuid): data = dict() if not vars(args).get('confirmed', None): @@ -73,8 +76,13 @@ def ceph_mon_add(cc, args): if ceph_mon_gib: data['ceph_mon_gib'] = ceph_mon_gib + data['ihost_uuid'] = ihost_uuid ceph_mon = cc.ceph_mon.create(**data) - suuid = getattr(ceph_mon, 'uuid', '') + if ceph_mon and len(ceph_mon.ceph_mon): + suuid = ceph_mon.ceph_mon[0].get('uuid', '') + else: + raise exc.CommandError( + "Created ceph_mon has invalid data.") try: ceph_mon = cc.ceph_mon.get(suuid) except exc.HTTPNotFound: diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/ceph_mon_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/ceph_mon_shell.py index ee5a7ff7cd..6bc4be515e 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/ceph_mon_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/ceph_mon_shell.py @@ -11,21 +11,24 @@ from cgtsclient.common import constants from cgtsclient.common import utils +from cgtsclient import exc from cgtsclient.v1 import ihost as ihost_utils def _print_ceph_mon_show(ceph_mon): fields = ['uuid', 'ceph_mon_gib', - 'created_at', 'updated_at'] + 'created_at', 'updated_at', + 'state', 'task'] data = [(f, getattr(ceph_mon, f)) for f in fields] utils.print_tuple_list(data) def _print_ceph_mon_list(cc): field_labels = ['uuid', 'ceph_mon_gib', - 'hostname'] - fields = ['uuid', 'ceph_mon_gib', 'hostname'] + 'hostname', 'state', 'task'] + fields = ['uuid', 'ceph_mon_gib', 'hostname', + 'state', 'task'] ceph_mons = cc.ceph_mon.list() utils.print_list(ceph_mons, fields, field_labels, sortby=0) @@ -88,3 +91,44 @@ def do_ceph_mon_show(cc, args): hostname = getattr(ceph_mon, 'hostname', '') if hostname == ihost.hostname: _print_ceph_mon_show(ceph_mon) + + +@utils.arg('hostnameorid', + metavar='', + help='name or ID of host [REQUIRED]') +def do_ceph_mon_add(cc, args): + ihost = ihost_utils._find_ihost(cc, args.hostnameorid) + + fields = {} + + fields['ihost_uuid'] = ihost.uuid + try: + ceph_mon = cc.ceph_mon.create(**fields) + except exc.HTTPNotFound: + raise exc.CommandError( + "Ceph mon creation failed: " + "host %s: " % args.hostnameorid) + + if ceph_mon and len(ceph_mon.ceph_mon): + suuid = ceph_mon.ceph_mon[0].get('uuid', '') + else: + raise exc.CommandError( + "Created ceph_mon has invalid data.") + try: + ceph_mon = cc.ceph_mon.get(suuid) + except exc.HTTPNotFound: + raise exc.CommandError("Created ceph monitor UUID not found: " + "%s" % suuid) + + _print_ceph_mon_show(ceph_mon) + + +@utils.arg('hostnameorid', + help='hostname for compute') +def do_ceph_mon_delete(cc, args): + ihost = ihost_utils._find_ihost(cc, args.hostnameorid) + + try: + cc.ceph_mon.delete(ihost.uuid) + except exc.HTTPNotFound: + raise exc.CommandError("failed to delete ceph_mon") diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/storage_backend.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/storage_backend.py index 8059d6797d..f8e4d57624 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/storage_backend.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/storage_backend.py @@ -12,6 +12,7 @@ from cgtsclient.common import constants from cgtsclient.common import utils from cgtsclient import exc from cgtsclient.v1 import ceph_mon as ceph_mon_utils +from cgtsclient.v1 import ihost as ihost_utils from cgtsclient.v1 import storage_ceph # noqa from cgtsclient.v1 import storage_ceph_external # noqa from cgtsclient.v1 import storage_external # noqa @@ -139,7 +140,20 @@ def backend_add(cc, backend, args): # add ceph mons to controllers if backend == constants.SB_TYPE_CEPH: - ceph_mon_utils.ceph_mon_add(cc, args) + # Controllers should always have monitors. + # Not finding a controller means it's not yet configured, + # so move forward. + try: + ihost = ihost_utils._find_ihost(cc, constants.CONTROLLER_0_HOSTNAME) + ceph_mon_utils.ceph_mon_add(cc, args, ihost.uuid) + except exc.CommandError: + pass + + try: + ihost = ihost_utils._find_ihost(cc, constants.CONTROLLER_1_HOSTNAME) + ceph_mon_utils.ceph_mon_add(cc, args, ihost.uuid) + except exc.CommandError: + pass # allowed storage_backend fields allowed_fields = ['name', 'services', 'confirmed', 'ceph_conf'] diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ceph_mon.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ceph_mon.py index 44b5a81ed1..6b3a529e4d 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ceph_mon.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ceph_mon.py @@ -16,10 +16,11 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2017 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # import jsonpatch +import six import pecan from pecan import rest @@ -75,6 +76,9 @@ class CephMon(base.APIBase): forihostid = int "The id of the host the ceph mon belongs to." + ihost_uuid = types.uuid + "The UUID of the host this ceph mon belongs to" + hostname = wtypes.text "The name of host this ceph mon belongs to." @@ -85,13 +89,11 @@ class CephMon(base.APIBase): ceph_mon_gib = int "The ceph-mon-lv size in GiB, for Ceph backend only." - ceph_mon_dev_ctrl0 = wtypes.text - "The disk device on controller-0 that cgts-vg will be extended " \ - "to create ceph-mon-lv" + state = wtypes.text + "The state of the monitor. It can be configured or configuring." - ceph_mon_dev_ctrl1 = wtypes.text - "The disk device on controller-1 that cgts-vg will be extended " \ - "to create ceph-mon-lv" + task = wtypes.text + "Current task of the corresponding ceph monitor." links = [link.Link] "A list containing a self link and associated ceph_mon links" @@ -100,28 +102,18 @@ class CephMon(base.APIBase): updated_at = wtypes.datetime.datetime def __init__(self, **kwargs): + + defaults = {'state': constants.SB_STATE_CONFIGURED, + 'task': constants.SB_TASK_NONE} + self.fields = objects.ceph_mon.fields.keys() for k in self.fields: - setattr(self, k, kwargs.get(k)) + setattr(self, k, kwargs.get(k, defaults.get(k))) if not self.uuid: self.uuid = uuidutils.generate_uuid() - self.fields.append('ceph_mon_dev') - setattr(self, 'ceph_mon_dev', kwargs.get('ceph_mon_dev', None)) - - self.fields.append('ceph_mon_dev_ctrl0') - setattr(self, 'ceph_mon_dev_ctrl0', - kwargs.get('ceph_mon_dev_ctrl0', None)) - - self.fields.append('ceph_mon_dev_ctrl1') - setattr(self, 'ceph_mon_dev_ctrl1', - kwargs.get('ceph_mon_dev_ctrl1', None)) - - self.fields.append('device_node') - setattr(self, 'device_node', kwargs.get('device_node', None)) - @classmethod def convert_with_links(cls, rpc_ceph_mon, expand=True): @@ -129,12 +121,15 @@ class CephMon(base.APIBase): if not expand: ceph_mon.unset_fields_except(['created_at', 'updated_at', + 'ihost_uuid', 'forihostid', 'uuid', 'device_path', 'device_node', 'ceph_mon_dev', 'ceph_mon_gib', + 'state', + 'task', 'ceph_mon_dev_ctrl0', 'ceph_mon_dev_ctrl1', 'hostname']) @@ -381,13 +376,21 @@ class CephMonController(rest.RestController): return StorageBackendConfig.get_ceph_mon_ip_addresses( pecan.request.dbapi) + @cutils.synchronized(LOCK_NAME) + @wsme_pecan.wsexpose(None, six.text_type, status_code=204) + def delete(self, host_uuid): + """Delete a ceph_mon.""" + + _delete(host_uuid) + def _set_defaults(ceph_mon): defaults = { + 'uuid': None, 'ceph_mon_gib': constants.SB_CEPH_MON_GIB, 'ceph_mon_dev': None, - 'ceph_mon_dev_ctrl0': None, - 'ceph_mon_dev_ctrl1': None, + 'state': constants.SB_STATE_CONFIGURED, + 'task': constants.SB_TASK_NONE, } storage_ceph_merged = ceph_mon.copy() @@ -407,30 +410,74 @@ def _create(ceph_mon): _check_ceph_mon(ceph_mon) + chost = pecan.request.dbapi.ihost_get(ceph_mon['ihost_uuid']) + ceph_mon['forihostid'] = chost['id'] + controller_fs_utils._check_controller_fs( ceph_mon_gib_new=ceph_mon['ceph_mon_gib']) pecan.request.rpcapi.reserve_ip_for_first_storage_node( pecan.request.context) - new_ceph_mons = list() - chosts = pecan.request.dbapi.ihost_get_by_personality(constants.CONTROLLER) - for chost in chosts: - # Check if mon exists - ceph_mons = pecan.request.dbapi.ceph_mon_get_by_ihost(chost.uuid) - if ceph_mons: - pecan.request.dbapi.ceph_mon_update( - ceph_mons[0].uuid, {'ceph_mon_gib': ceph_mon['ceph_mon_gib']} - ) - new_ceph_mons.append(ceph_mons[0]) - else: - ceph_mon_new = dict() - ceph_mon_new['uuid'] = None - ceph_mon_new['forihostid'] = chost.id - ceph_mon_new['ceph_mon_gib'] = ceph_mon['ceph_mon_gib'] + # Size of ceph-mon logical volume must be the same for all + # monitors so we get the size from any or use default. + ceph_mons = pecan.request.dbapi.ceph_mon_get_list() + if ceph_mons: + ceph_mon['ceph_mon_gib'] = ceph_mons[0]['ceph_mon_gib'] - LOG.info("creating ceph_mon_new for %s: %s" % - (chost.hostname, str(ceph_mon_new))) - new_ceph_mons.append(pecan.request.dbapi.ceph_mon_create(ceph_mon_new)) + # In case we add the monitor on a worker node, the state + # and task must be set properly. + if chost.personality == constants.WORKER: + ceph_mon['state'] = constants.SB_STATE_CONFIGURING + ctrls = pecan.request.dbapi.ihost_get_by_personality( + constants.CONTROLLER) + valid_ctrls = [ + ctrl for ctrl in ctrls if + (ctrl.administrative == constants.ADMIN_LOCKED and + ctrl.availability == constants.AVAILABILITY_ONLINE) or + (ctrl.administrative == constants.ADMIN_UNLOCKED and + ctrl.operational == constants.OPERATIONAL_ENABLED)] - return new_ceph_mons + tasks = {} + for ctrl in valid_ctrls: + tasks[ctrl.hostname] = constants.SB_STATE_CONFIGURING + + ceph_mon['task'] = str(tasks) + + LOG.info("Creating ceph-mon DB entry for host uuid %s: %s" % + (ceph_mon['ihost_uuid'], str(ceph_mon))) + new_ceph_mon = pecan.request.dbapi.ceph_mon_create(ceph_mon) + + # We update the base config when adding a dynamic monitor. + # At this moment the only possibility to add a dynamic monitor + # is on a worker node, so we check for that. + if chost.personality == constants.WORKER: + # Storage nodes are not supported on a controller based + # storage model. + personalities = [constants.CONTROLLER, constants.WORKER] + pecan.request.rpcapi.update_ceph_base_config( + pecan.request.context, + personalities) + + # The return value needs to be iterable, so make it a list. + return [new_ceph_mon] + + +def _delete(host_uuid): + ceph_mon = pecan.request.dbapi.ceph_mon_get_by_ihost(host_uuid) + if ceph_mon: + ceph_mon = ceph_mon[0] + else: + raise wsme.exc.ClientSideError( + _("No Ceph Monitor defined for host with uuid: %s" % host_uuid)) + + if ceph_mon.state == constants.SB_STATE_CONFIG_ERR: + try: + pecan.request.dbapi.ceph_mon_destroy(ceph_mon.uuid) + except exception.HTTPNotFound: + raise wsme.exc.ClientSideError("Deleting Ceph Monitor failed!") + else: + raise wsme.exc.ClientSideError( + _("Direct Ceph monitor delete only allowed for state '%s'. " + "Please lock and delete node to remove the configured Ceph Monitor." + % constants.SB_STATE_CONFIG_ERR)) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py index b5336a9a4c..2216e02692 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py @@ -4452,8 +4452,8 @@ class HostController(rest.RestController): LOG.info( 'Apply new Ceph manifest to provisioned worker nodes.' ) - pecan.request.rpcapi.config_worker_for_ceph( - pecan.request.context + pecan.request.rpcapi.update_ceph_base_config( + pecan.request.context, personalities=[constants.WORKER] ) # mark all tasks completed after updating the manifests for # all worker nodes. diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index cb9f5e4ede..1022c91346 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -1415,24 +1415,29 @@ class ConductorManager(service.PeriodicService): ceph_mon_gib = ceph_mons[0].ceph_mon_gib values = {'forisystemid': system.id, 'forihostid': host.id, - 'ceph_mon_gib': ceph_mon_gib} + 'ceph_mon_gib': ceph_mon_gib, + 'state': constants.SB_STATE_CONFIGURED, + 'task': constants.SB_TASK_NONE} LOG.info("creating ceph_mon for host %s with ceph_mon_gib=%s." % (host.hostname, ceph_mon_gib)) self.dbapi.ceph_mon_create(values) - def config_worker_for_ceph(self, context): - """ - configure worker nodes for adding ceph - :param context: - :return: none - """ - personalities = [constants.WORKER] - config_uuid = self._config_update_hosts(context, personalities) - config_dict = { - "personalities": personalities, - "classes": ['platform::ceph::compute::runtime'] - } - self._config_apply_runtime_manifest(context, config_uuid, config_dict) + def _remove_ceph_mon(self, host): + if not StorageBackendConfig.has_backend( + self.dbapi, + constants.CINDER_BACKEND_CEPH + ): + return + + mon = self.dbapi.ceph_mon_get_by_ihost(host.uuid) + if mon: + LOG.info("Deleting ceph monitor for host %s" + % str(host.hostname)) + self.dbapi.ceph_mon_destroy(mon[0].uuid) + else: + LOG.info("No ceph monitor present for host %s. " + "Skipping deleting ceph monitor." + % str(host.hostname)) def update_remotelogging_config(self, context): """Update the remotelogging configuration""" @@ -1529,6 +1534,8 @@ class ConductorManager(service.PeriodicService): self._allocate_addresses_for_host(context, host) # Set up the PXE config file for this host so it can run the installer self._update_pxe_config(host) + if host['hostname'] == constants.STORAGE_0_HOSTNAME: + self._ceph_mon_create(host) # TODO(CephPoolsDecouple): remove def configure_osd_pools(self, context, ceph_backend=None, new_pool_size=None, new_pool_min_size=None): @@ -1590,6 +1597,7 @@ class ConductorManager(service.PeriodicService): self._remove_addresses_for_host(host) self._puppet.remove_host_config(host) self._remove_pxe_config(host) + self._remove_ceph_mon(host) def _unconfigure_storage_host(self, host): """Unconfigure a storage host. @@ -5649,7 +5657,7 @@ class ConductorManager(service.PeriodicService): rpcapi.iconfig_update_install_uuid(context, host_uuid, install_uuid) def update_ceph_config(self, context, sb_uuid, services): - """Update the manifests for Cinder Ceph backend""" + """Update the manifests for Ceph backend and services""" personalities = [constants.CONTROLLER] @@ -5666,7 +5674,7 @@ class ConductorManager(service.PeriodicService): 'platform::haproxy::runtime', 'openstack::keystone::endpoint::runtime', 'platform::filesystem::img_conversions::runtime', - 'platform::ceph::controller::runtime', + 'platform::ceph::runtime', ] if utils.is_aio_duplex_system(self.dbapi): @@ -5721,6 +5729,27 @@ class ConductorManager(service.PeriodicService): 'task': str(tasks)} self.dbapi.storage_ceph_update(sb_uuid, values) + def update_ceph_base_config(self, context, personalities): + """ Update Ceph configuration, monitors and ceph.conf only""" + config_uuid = self._config_update_hosts(context, personalities) + + valid_nodes = [] + for personality in personalities: + nodes = self.dbapi.ihost_get_by_personality(personality) + valid_nodes += [ + node for node in nodes if + (node.administrative == constants.ADMIN_UNLOCKED and + node.operational == constants.OPERATIONAL_ENABLED)] + + # TODO: check what other puppet class need to be called + config_dict = { + "personalities": personalities, + "host_uuids": [node.uuid for node in valid_nodes], + "classes": ['platform::ceph::runtime'], + puppet_common.REPORT_STATUS_CFG: puppet_common.REPORT_CEPH_MONITOR_CONFIG + } + self._config_apply_runtime_manifest(context, config_uuid, config_dict) + def config_update_nova_local_backed_hosts(self, context, instance_backing): hosts_uuid = self.hosts_with_nova_local(instance_backing) if hosts_uuid: @@ -6001,6 +6030,19 @@ class ConductorManager(service.PeriodicService): LOG.error("No match for sysinv-agent manifest application reported! " "reported_cfg: %(cfg)s status: %(status)s " "iconfig: %(iconfig)s" % args) + elif reported_cfg == puppet_common.REPORT_CEPH_MONITOR_CONFIG: + host_uuid = iconfig['host_uuid'] + if status == puppet_common.REPORT_SUCCESS: + # Configuration was successful + self.report_ceph_base_config_success(host_uuid) + elif status == puppet_common.REPORT_FAILURE: + # Configuration has failed + self.report_ceph_base_config_failure(host_uuid, error) + else: + args = {'cfg': reported_cfg, 'status': status, 'iconfig': iconfig} + LOG.error("No match for sysinv-agent manifest application reported! " + "reported_cfg: %(cfg)s status: %(status)s " + "iconfig: %(iconfig)s" % args) else: LOG.error("Reported configuration '%(cfg)s' is not handled by" " report_config_status! iconfig: %(iconfig)s" % @@ -6495,6 +6537,89 @@ class ConductorManager(service.PeriodicService): values = {'state': constants.SB_STATE_CONFIG_ERR, 'task': None} self.dbapi.storage_backend_update(backend.uuid, values) + def report_ceph_base_config_success(self, host_uuid): + """ + Callback for Sysinv Agent + """ + + LOG.info("Ceph monitor update succeeded on host: %s" % host_uuid) + + # Get the monitor that is configuring + monitor_list = self.dbapi.ceph_mon_get_list() + monitor = None + for mon in monitor_list: + if mon.state == constants.SB_STATE_CONFIGURING: + monitor = mon + break + + ctrls = self.dbapi.ihost_get_by_personality(constants.CONTROLLER) + # Note that even if nodes are degraded we still accept the answer. + valid_ctrls = [ctrl for ctrl in ctrls if + (ctrl.administrative == constants.ADMIN_LOCKED and + ctrl.availability == constants.AVAILABILITY_ONLINE) or + (ctrl.administrative == constants.ADMIN_UNLOCKED and + ctrl.operational == constants.OPERATIONAL_ENABLED)] + + # Set state for current node + for host in valid_ctrls: + if host.uuid == host_uuid: + break + else: + LOG.error("Host %(host) is not in the required state!" % host_uuid) + host = self.dbapi.ihost_get(host_uuid) + if not host: + LOG.error("Host %s is invalid!" % host_uuid) + return + elif host.personality == constants.WORKER: + LOG.info("Ignoring report from worker hosts") + return + tasks = eval(monitor.get('task', '{}')) + if tasks: + tasks[host.hostname] = constants.SB_STATE_CONFIGURED + else: + tasks = {host.hostname: constants.SB_STATE_CONFIGURED} + + # Check if all hosts configurations have applied correctly + # and mark config success + config_success = True + for host in valid_ctrls: + if tasks.get(host.hostname, '') != constants.SB_STATE_CONFIGURED: + config_success = False + + values = None + if monitor.state != constants.SB_STATE_CONFIG_ERR: + if config_success: + # All hosts have completed configuration + values = {'state': constants.SB_STATE_CONFIGURED, 'task': None} + else: + # This host_uuid has completed configuration + values = {'task': str(tasks)} + if values: + self.dbapi.ceph_mon_update(monitor.uuid, values) + + def report_ceph_base_config_failure(self, host_uuid, error): + """ + Callback for Sysinv Agent + """ + LOG.error("Ceph monitor update failed on host: %(host)s. Error: " + "%(error)s" % {'host': host_uuid, 'error': error}) + + host = self.dbapi.ihost_get(host_uuid) + if host and host.personality == constants.WORKER: + # Ignoring report from worker + return + + monitor_list = self.dbapi.ceph_mon_get_list() + monitor = None + for mon in monitor_list: + if mon.state == constants.SB_STATE_CONFIGURING: + monitor = mon + break + + # Set monitor to error state + values = {'state': constants.SB_STATE_CONFIG_ERR, 'task': None} + self.dbapi.ceph_mon_update(monitor.uuid, values) + def create_controller_filesystems(self, context, rootfs_device): """ Create the storage config based on disk size for database, image, backup, img-conversion diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py index e95fe10314..aa0544ed45 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py @@ -788,13 +788,19 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): """ return self.call(context, self.make_msg('update_lvm_config')) - def config_worker_for_ceph(self, context): - """Synchronously, have the conductor update the worker configuration - for adding ceph. + def update_ceph_base_config(self, context, personalities): + """Synchronously, have the conductor update the configuration + for monitors and ceph.conf. :param context: request context. + :param personalities: list of host personalities. """ - return self.call(context, self.make_msg('config_worker_for_ceph')) + return self.call( + context, self.make_msg( + 'update_ceph_base_config', + personalities=personalities + ) + ) def update_drbd_config(self, context): """Synchronously, have the conductor update the drbd configuration. diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/083_ceph_mon_tasks.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/083_ceph_mon_tasks.py new file mode 100644 index 0000000000..43fe754088 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/083_ceph_mon_tasks.py @@ -0,0 +1,26 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from sqlalchemy import Column, MetaData, String, Table + +ENGINE = 'InnoDB' +CHARSET = 'utf8' + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + ceph_mon = Table('ceph_mon', meta, autoload=True) + ceph_mon.create_column(Column('state', String(255))) + ceph_mon.create_column(Column('task', String(255))) + + +def downgrade(migrate_engine): + # As per other openstack components, downgrade is + # unsupported in this release. + raise NotImplementedError('SysInv database downgrade is unsupported.') diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py index beff9361ea..80f02b8053 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py @@ -953,6 +953,8 @@ class CephMon(Base): uuid = Column(String(36)) device_path = Column(String(255)) ceph_mon_gib = Column(Integer) + state = Column(String(255)) + task = Column(String(255)) forihostid = Column(Integer, ForeignKey('i_host.id', ondelete='CASCADE')) host = relationship("ihost", lazy="joined", join_depth=1) diff --git a/sysinv/sysinv/sysinv/sysinv/objects/ceph_mon.py b/sysinv/sysinv/sysinv/sysinv/objects/ceph_mon.py index 577f2a9ec1..530ad79874 100644 --- a/sysinv/sysinv/sysinv/sysinv/objects/ceph_mon.py +++ b/sysinv/sysinv/sysinv/sysinv/objects/ceph_mon.py @@ -23,13 +23,17 @@ class CephMon(base.SysinvObject): 'device_path': utils.str_or_none, 'ceph_mon_gib': utils.int_or_none, + 'state': utils.str_or_none, + 'task': utils.str_or_none, 'forihostid': utils.int_or_none, + 'ihost_uuid': utils.str_or_none, 'hostname': utils.str_or_none, } _foreign_fields = { 'hostname': 'host:hostname', + 'ihost_uuid': 'host:uuid' } @base.remotable_classmethod diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/common.py b/sysinv/sysinv/sysinv/sysinv/puppet/common.py index 476ee19a0a..e6a08a665b 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/common.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/common.py @@ -33,6 +33,7 @@ REPORT_EXTERNAL_BACKEND_CONFIG = 'external_config' REPORT_CEPH_BACKEND_CONFIG = 'ceph_config' REPORT_CEPH_EXTERNAL_BACKEND_CONFIG = 'ceph_external_config' REPORT_CEPH_SERVICES_CONFIG = 'ceph_services' +REPORT_CEPH_MONITOR_CONFIG = 'ceph_monitor' def puppet_apply_manifest(ip_address, personality,