From 10a595f22da80959ffb73241bcd26b997545f00b Mon Sep 17 00:00:00 2001 From: Kristine Bujold Date: Thu, 27 Jun 2019 14:07:51 -0400 Subject: [PATCH] Create new host_fs postgres table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a new host-fs postgres table to store information about a host’s filesystem. Replicated filesystem will continue to be stored in the controller_fs table. This commit creates the table. The sysinv agent creates the docker, backup and scratch filesystem for each supported host. If the filesystems have changed the audit triggers an update. These changes were testing with AIO-SX and Standard lab installs. Change-Id: I8167dfe74688c05fd8b747caac3d418d2749a740 Partial-Bug: 1830142 Signed-off-by: Kristine Bujold --- sysinv/sysinv/centos/build_srpm.data | 2 +- sysinv/sysinv/sysinv/sysinv/agent/manager.py | 62 ++++++++++- .../sysinv/sysinv/sysinv/common/constants.py | 24 +++- .../sysinv/sysinv/sysinv/common/exception.py | 10 +- sysinv/sysinv/sysinv/sysinv/common/utils.py | 42 +++++++ .../sysinv/sysinv/sysinv/conductor/manager.py | 60 +++++++++- .../sysinv/sysinv/sysinv/conductor/rpcapi.py | 20 +++- sysinv/sysinv/sysinv/sysinv/db/api.py | 90 ++++++++++++++- .../sysinv/sysinv/sysinv/db/sqlalchemy/api.py | 105 +++++++++++++++++- .../migrate_repo/versions/089_host_fs.py | 61 ++++++++++ .../sysinv/sysinv/db/sqlalchemy/models.py | 16 ++- .../sysinv/sysinv/sysinv/objects/__init__.py | 5 +- .../sysinv/sysinv/sysinv/objects/host_fs.py | 38 +++++++ 13 files changed, 519 insertions(+), 16 deletions(-) create mode 100644 sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/089_host_fs.py create mode 100644 sysinv/sysinv/sysinv/sysinv/objects/host_fs.py diff --git a/sysinv/sysinv/centos/build_srpm.data b/sysinv/sysinv/centos/build_srpm.data index f3c2c4e149..7792898404 100644 --- a/sysinv/sysinv/centos/build_srpm.data +++ b/sysinv/sysinv/centos/build_srpm.data @@ -1,2 +1,2 @@ SRC_DIR="sysinv" -TIS_PATCH_VER=325 +TIS_PATCH_VER=326 diff --git a/sysinv/sysinv/sysinv/sysinv/agent/manager.py b/sysinv/sysinv/sysinv/sysinv/agent/manager.py index 7fe3655a15..0bc30a4968 100644 --- a/sysinv/sysinv/sysinv/sysinv/agent/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/agent/manager.py @@ -17,7 +17,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # @@ -146,12 +146,14 @@ class AgentManager(service.PeriodicService): self._iconfig_read_config_reported = None self._ihost_personality = None self._ihost_uuid = "" + self._ihost_rootfs_device = "" self._agent_throttle = 0 self._mgmt_ip = None self._prev_disk = None self._prev_partition = None self._prev_lvg = None self._prev_pv = None + self._prev_fs = None self._subfunctions = None self._subfunctions_configured = False self._notify_subfunctions_alarm_clear = False @@ -745,6 +747,7 @@ class AgentManager(service.PeriodicService): self._ihost_uuid = ihost['uuid'] self._ihost_personality = ihost['personality'] self._mgmt_ip = ihost['mgmt_ip'] + self._ihost_rootfs_device = ihost['rootfs_device'] if os.path.isfile(tsc.PLATFORM_CONF_FILE): # read the platform config file and check for UUID @@ -1157,8 +1160,8 @@ class AgentManager(service.PeriodicService): LOG.debug("SysInv Agent Audit running.") if force_updates: - LOG.debug("SysInv Agent Audit force updates: (%s)" % - (', '.join(force_updates))) + LOG.info("SysInv Agent Audit force updates: (%s)" % + (', '.join(force_updates))) self._update_ttys_dcd_status(icontext, self._ihost_uuid) if self._agent_throttle > 5: @@ -1182,7 +1185,7 @@ class AgentManager(service.PeriodicService): self._prev_disk = None # if this audit is requested by conductor, clear - # previous states for disk, lvg and pv to force an update + # previous states for disk, lvg, pv and fs to force an update if force_updates: if constants.DISK_AUDIT_REQUEST in force_updates: self._prev_disk = None @@ -1192,6 +1195,8 @@ class AgentManager(service.PeriodicService): self._prev_pv = None if constants.PARTITION_AUDIT_REQUEST in force_updates: self._prev_partition = None + if constants.FILESYSTEM_AUDIT_REQUEST in force_updates: + self._prev_fs = None # Update disks idisk = self._idisk_operator.idisk_get() @@ -1250,6 +1255,55 @@ class AgentManager(service.PeriodicService): self._prev_lvg = None pass + # Update the filesystems + + # Get the supported filesystems for this host + filesystems = [] + + # check if the scratch fs is supported for current host + if utils.is_filesystem_supported(constants.FILESYSTEM_NAME_SCRATCH, self._ihost_personality): + scratch_lv_size = utils.get_controller_fs_scratch_size() + data = { + 'name': constants.FILESYSTEM_NAME_SCRATCH, + 'size': scratch_lv_size, + 'logical_volume': constants.FILESYSTEM_LV_DICT[ + constants.FILESYSTEM_NAME_SCRATCH] + } + filesystems.append(data) + + # check if the backup fs is supported for current host + if utils.is_filesystem_supported(constants.FILESYSTEM_NAME_BACKUP, self._ihost_personality): + backup_lv_size = utils.get_controller_fs_backup_size(self._ihost_rootfs_device) + data = { + 'name': constants.FILESYSTEM_NAME_BACKUP, + 'size': backup_lv_size, + 'logical_volume': constants.FILESYSTEM_LV_DICT[ + constants.FILESYSTEM_NAME_BACKUP] + } + filesystems.append(data) + + # check if the docker fs is supported for current host + if utils.is_filesystem_supported(constants.FILESYSTEM_NAME_DOCKER, self._ihost_personality): + data = { + 'name': constants.FILESYSTEM_NAME_DOCKER, + 'size': constants.KUBERNETES_DOCKER_STOR_SIZE, + 'logical_volume': constants.FILESYSTEM_LV_DICT[ + constants.FILESYSTEM_NAME_DOCKER] + } + filesystems.append(data) + + if filesystems and ((self._prev_fs is None) or (self._prev_fs != filesystems)): + try: + rpcapi.create_host_filesystems(icontext, + self._ihost_uuid, + filesystems) + self._prev_fs = filesystems + except exception.SysinvException: + LOG.exception("Sysinv Agent exception updating fs" + "conductor.") + self._prev_fs = None + pass + self._report_config_applied(icontext) if os.path.isfile(tsc.PLATFORM_CONF_FILE): diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index 30fc6f074d..1439e57357 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -526,6 +526,26 @@ FILESYSTEM_LV_DICT = { FILESYSTEM_NAME_PATCH_VAULT: 'patch-vault-lv', } +FILESYSTEM_CONTROLLER_SUPPORTED_LIST = [ + FILESYSTEM_NAME_SCRATCH, + FILESYSTEM_NAME_BACKUP, + FILESYSTEM_NAME_DOCKER, +] + +FILESYSTEM_WORKER_SUPPORTED_LIST = [ + FILESYSTEM_NAME_DOCKER, +] + +FILESYSTEM_STORAGE_SUPPORTED_LIST = [ + FILESYSTEM_NAME_DOCKER, +] + +FILESYSTEM_HOSTS_SUPPORTED_LIST_DICT = { + CONTROLLER: FILESYSTEM_CONTROLLER_SUPPORTED_LIST, + WORKER: FILESYSTEM_WORKER_SUPPORTED_LIST, + STORAGE: FILESYSTEM_STORAGE_SUPPORTED_LIST, +} + SUPPORTED_LOGICAL_VOLUME_LIST = FILESYSTEM_LV_DICT.values() SUPPORTED_FILEYSTEM_LIST = [ @@ -599,10 +619,12 @@ DISK_AUDIT_REQUEST = "audit_disk" LVG_AUDIT_REQUEST = "audit_lvg" PV_AUDIT_REQUEST = "audit_pv" PARTITION_AUDIT_REQUEST = "audit_partition" +FILESYSTEM_AUDIT_REQUEST = "audit_fs" CONTROLLER_AUDIT_REQUESTS = [DISK_AUDIT_REQUEST, LVG_AUDIT_REQUEST, PV_AUDIT_REQUEST, - PARTITION_AUDIT_REQUEST] + PARTITION_AUDIT_REQUEST, + FILESYSTEM_AUDIT_REQUEST] # Interface definitions NETWORK_TYPE_NONE = 'none' diff --git a/sysinv/sysinv/sysinv/sysinv/common/exception.py b/sysinv/sysinv/sysinv/sysinv/common/exception.py index 5c074d0d0f..edd89c534d 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/exception.py +++ b/sysinv/sysinv/sysinv/sysinv/common/exception.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. @@ -1364,3 +1364,11 @@ class UnsupportedAssignedInterfaceDataNetworkType(Conflict): class UnsupportedRemovedInterfaceDataNetworkType(Conflict): message = _("Cannot remove datanetwork with type '%(network_type)s' " "from an interface.") + + +class FilesystemAlreadyExists(Conflict): + message = _("A Host FS with name %(name)s already exists.") + + +class FilesystemNotFound(NotFound): + message = _("No Host FS with id %(fs_id)s not found") diff --git a/sysinv/sysinv/sysinv/sysinv/common/utils.py b/sysinv/sysinv/sysinv/sysinv/common/utils.py index cfa0bbcf89..09c90f31eb 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/common/utils.py @@ -1605,6 +1605,16 @@ def get_dhcp_client_iaid(mac_address): return hwaddr[2] << 24 | hwaddr[3] << 16 | hwaddr[4] << 8 | hwaddr[5] +def is_filesystem_supported(fs, personality): + """ Check to see if a filesystem is supported for the host personality. + """ + + if personality in constants.FILESYSTEM_HOSTS_SUPPORTED_LIST_DICT: + if fs in constants.FILESYSTEM_HOSTS_SUPPORTED_LIST_DICT[personality]: + return True + return False + + def get_controller_fs_scratch_size(): """ Get the filesystem scratch size setup by kickstart. """ @@ -1636,6 +1646,38 @@ def get_controller_fs_scratch_size(): return scratch_gib +def get_controller_fs_backup_size(rootfs_device): + """ Get the filesystem backup size. + """ + + disk_size = get_disk_capacity_mib(rootfs_device) + disk_size = int(disk_size / 1024) + + if disk_size > constants.DEFAULT_SMALL_DISK_SIZE: + LOG.debug("Disk size : %s ... large disk defaults" % disk_size) + + database_storage = constants.DEFAULT_DATABASE_STOR_SIZE + + cgcs_lv_size = constants.DEFAULT_CGCS_STOR_SIZE + backup_lv_size = database_storage + cgcs_lv_size + \ + constants.BACKUP_OVERHEAD + + elif disk_size >= constants.MINIMUM_DISK_SIZE: + + LOG.debug("Disk size : %s ... small disk defaults" % disk_size) + + # Due to the small size of the disk we can't provide the + # proper amount of backup space which is (database + cgcs_lv + # + BACKUP_OVERHEAD) so we are using a smaller default. + backup_lv_size = constants.DEFAULT_SMALL_BACKUP_STOR_SIZE + + else: + LOG.info("Disk size : %s ... disk too small" % disk_size) + raise exception.SysinvException("Disk size requirements not met.") + + return backup_lv_size + + def get_cgts_vg_free_space(): """Determine free space in cgts-vg""" diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index 6a555c8e79..f148c29e66 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -3302,6 +3302,61 @@ class ConductorManager(service.PeriodicService): return + def create_host_filesystems(self, context, + ihost_uuid, fs_dict_array): + """Create a filesystems for an ihost with the supplied data. + + This method allows records for filesystems for ihost to be + created. + + :param context: an admin context + :param ihost_uuid: ihost uuid unique id + :param fs_dict_array: initial values for filesystems group objects + :returns: pass or fail + """ + + ihost_uuid.strip() + try: + ihost = self.dbapi.ihost_get(ihost_uuid) + except exception.ServerNotFound: + LOG.exception("Invalid ihost_uuid %s" % ihost_uuid) + return + + host_fs_list = self.dbapi.host_fs_get_by_ihost(ihost_uuid) + forihostid = ihost['id'] + + for fs in fs_dict_array: + fs_dict = { + 'forihostid': forihostid, + } + fs_dict.update(fs) + found = False + + for host_fs in host_fs_list: + if host_fs.name == fs['name']: + found = True + LOG.debug("Host FS '%s' already exists" % fs['name']) + if host_fs.size != fs['size']: + LOG.info("Host FS uuid: %s changed size from %s to %s", + host_fs.uuid, host_fs.size, fs['size']) + # Update the database + try: + self.dbapi.host_fs_update(host_fs.id, fs_dict) + except Exception: + LOG.exception("Host FS Update failed") + break + if not found: + try: + + LOG.info("Creating Host FS:%s:%s %d for host id %d" % + (fs_dict['name'], fs_dict['logical_volume'], + fs_dict['size'], fs_dict['forihostid'])) + self.dbapi.host_fs_create(forihostid, fs_dict) + except Exception: + LOG.exception("Host FS Creation failed") + + return + def _fill_partition_info(self, db_part, ipart): db_part_dict = db_part.as_dict() keys = ['start_mib', 'end_mib', 'size_mib', 'type_name', 'type_guid'] @@ -4160,7 +4215,7 @@ class ConductorManager(service.PeriodicService): @periodic_task.periodic_task(spacing=CONF.conductor.audit_interval) def _agent_update_request(self, context): """ - Check DB for inventory objects with an inconsistent state and + Check DB for inventory objects with an inconsistent state and request an update from sysinv agent. Currently requesting updates for: - ipv: if state is not 'provisioned' @@ -4212,6 +4267,9 @@ class ConductorManager(service.PeriodicService): ilvgs = self.dbapi.ilvg_get_by_ihost(host.uuid) if not ilvgs: update_hosts_dict(host.id, constants.LVG_AUDIT_REQUEST) + host_fs = self.dbapi.host_fs_get_by_ihost(host.uuid) + if not host_fs: + update_hosts_dict(host.id, constants.FILESYSTEM_AUDIT_REQUEST) # Check partitions. partitions = self.dbapi.partition_get_all() diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py index ad18adf480..0ac9f87437 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py @@ -16,7 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # """ @@ -176,6 +176,24 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): self.make_msg('create_controller_filesystems', rootfs_device=rootfs_device)) + def create_host_filesystems(self, context, ihost_uuid, fs_dict_array): + """Create or update the filesystem for an ihost with the supplied + data. + + This method allows records for a filesystem for ihost to be + created, or updated. + + :param context: an admin context + :param ihost_uuid: ihost uuid unique id + :param fs_dict_array: initial values for the filesystems + :returns: pass or fail + """ + + return self.call(context, + self.make_msg('create_host_filesystems', + ihost_uuid=ihost_uuid, + fs_dict_array=fs_dict_array)) + def get_ihost_by_macs(self, context, ihost_macs): """Finds ihost db entry based upon the mac list diff --git a/sysinv/sysinv/sysinv/sysinv/db/api.py b/sysinv/sysinv/sysinv/sysinv/db/api.py index c524aa60ff..25471f8caa 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/api.py +++ b/sysinv/sysinv/sysinv/sysinv/db/api.py @@ -16,7 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # @@ -2228,9 +2228,10 @@ class Connection(object): :param values: A dict containing several items used to identify and track the controller_fs. Example: - values = {'name': constants.DEFAULT_DOCKER_STOR_SIZE, + values = {'name': constants.FILESYSTEM_NAME_DOCKER, 'size': 30, - 'logical_volume': constants.FILESYSTEM_NAME_LV_DICT, + 'logical_volume': constants.FILESYSTEM_LV_DICT[ + constants.FILESYSTEM_NAME_DOCKER], 'replicated': False} :returns: A controller_fs. """ @@ -4339,3 +4340,86 @@ class Connection(object): :param uuid: The uuid of an interface network association. """ + + @abc.abstractmethod + def host_fs_create(self, forihostid, values): + """Create a new filesystem for a host. + + :param forihostid: uuid or id of an ihost + :param values: A dict containing several items used to identify + and track the filesystem. + Example: + values = {'name': constants.FILESYSTEM_NAME_DOCKER, + 'size': 30, + 'logical_volume': constants.FILESYSTEM_LV_DICT[ + constants.FILESYSTEM_NAME_DOCKER], + 'forihostid': 1} + :returns: A filesystem. + """ + + @abc.abstractmethod + def host_fs_get(self, fs_id): + """Return a filesystem. + + :param fs_id: The id or uuid of a filesystem. + :returns: A filesystem. + """ + + @abc.abstractmethod + def host_fs_get_all(self, forihostid=None): + """Return filesystems. + + :param forihostid: The id or uuid of an ihost. + :returns: filesystem. + """ + + @abc.abstractmethod + def host_fs_get_list(self, limit=None, marker=None, + sort_key=None, sort_dir=None): + """Return a list of filesystems. + + :param limit: Maximum number of filesystems to return. + :param marker: the last item of the previous page; we return the next + result set. + :param sort_key: Attribute by which results should be sorted. + :param sort_dir: direction in which results should be sorted. + (asc, desc) + """ + + @abc.abstractmethod + def host_fs_get_by_ihost(self, ihost, limit=None, + marker=None, sort_key=None, + sort_dir=None): + """List all the filesystems for a given ihost. + + :param ihost: The id or uuid of an ihost. + :param marker: the last item of the previous page; we return the next + result set. + :param sort_key: Attribute by which results should be sorted + :param sort_dir: direction in which results should be sorted + (asc, desc) + :returns: A list of filesystems. + """ + + @abc.abstractmethod + def host_fs_update(self, fs_id, values): + """Update properties of a filesystem. + + :param fs_id: The id or uuid of an filesystem. + :param values: Dict of values to update. May be a partial list. + Example: + values = {'name': constants.FILESYSTEM_NAME_DOCKER, + 'size': 30, + 'logical_volume': constants.FILESYSTEM_LV_DICT[ + constants.FILESYSTEM_NAME_DOCKER + ], + 'forihostid': 1} + :returns: A filesystem. + """ + + @abc.abstractmethod + def host_fs_destroy(self, fs_id): + """Destroy a filesystem. + + :param fs_id: The id or uuid of a filesystem. + """ diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py index 310344925e..8bf0fadfc7 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py @@ -15,7 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # """SQLAlchemy storage backend.""" @@ -148,7 +148,8 @@ def add_identity_filter(query, value, use_sensorgroupname=False, use_sensorname=False, use_cluster_uuid=False, - use_pciaddr=False): + use_pciaddr=False, + use_fsname=False): """Adds an identity filter to a query. Filters results by ID, if supplied value is a valid integer. @@ -189,6 +190,8 @@ def add_identity_filter(query, value, return query.filter_by(sensorname=value) elif use_pciaddr: return query.filter_by(pciaddr=value) + elif use_fsname: + return query.filter_by(name=value) else: return query.filter_by(hostname=value) @@ -1121,6 +1124,25 @@ def add_label_filter_by_host(query, hostid): return query.filter(models.ihost.uuid == hostid) +def add_host_fs_filter(query, value): + """Adds an fs-specific filter to a query. + + :param query: Initial query to add filter to. + :param value: Value for filtering results by. + :return: Modified query. + """ + return add_identity_filter(query, value, use_fsname=True) + + +def add_host_fs_filter_by_ihost(query, value): + if utils.is_int_like(value): + return query.filter_by(forihostid=value) + else: + query = query.join(models.ihost, + models.HostFs.forihostid == models.ihost.id) + return query.filter(models.ihost.uuid == value) + + class Connection(api.Connection): """SqlAlchemy connection.""" @@ -7934,3 +7956,82 @@ class Connection(api.Connection): @objects.objectify(objects.interface_datanetwork) def interface_datanetwork_query(self, values): return self._interface_datanetwork_query(values) + + def _host_fs_get(self, fs_id): + query = model_query(models.HostFs) + query = add_identity_filter(query, fs_id) + + try: + result = query.one() + except NoResultFound: + raise exception.FilesystemNotFound(fs_id=fs_id) + + return result + + @objects.objectify(objects.host_fs) + def host_fs_create(self, forihostid, values): + if not values.get('uuid'): + values['uuid'] = uuidutils.generate_uuid() + values['forihostid'] = int(forihostid) + fs = models.HostFs() + fs.update(values) + with _session_for_write() as session: + try: + session.add(fs) + session.flush() + except db_exc.DBDuplicateEntry: + raise exception.FilesystemAlreadyExists( + name=values['name'], host=forihostid) + + return self._host_fs_get(values['uuid']) + + @objects.objectify(objects.host_fs) + def host_fs_get_all(self, forihostid=None): + query = model_query(models.HostFs, read_deleted="no") + if forihostid: + query = query.filter_by(forihostid=forihostid) + return query.all() + + @objects.objectify(objects.host_fs) + def host_fs_get(self, fs_id): + return self._host_fs_get(fs_id) + + @objects.objectify(objects.host_fs) + def host_fs_get_list(self, limit=None, marker=None, + sort_key=None, sort_dir=None): + return _paginate_query(models.HostFs, limit, marker, + sort_key, sort_dir) + + @objects.objectify(objects.host_fs) + def host_fs_get_by_ihost(self, ihost, limit=None, marker=None, + sort_key=None, sort_dir=None): + + query = model_query(models.HostFs) + query = add_host_fs_filter_by_ihost(query, ihost) + return _paginate_query(models.HostFs, limit, marker, + sort_key, sort_dir, query) + + @objects.objectify(objects.host_fs) + def host_fs_update(self, fs_id, values): + with _session_for_write() as session: + query = model_query(models.HostFs, read_deleted="no", + session=session) + query = add_host_fs_filter(query, fs_id) + + count = query.update(values, synchronize_session='fetch') + if count != 1: + raise exception.FilesystemNotFound(fs_id=fs_id) + return query.one() + + def host_fs_destroy(self, fs_id): + with _session_for_write() as session: + # Delete physically since it has unique columns + if uuidutils.is_uuid_like(fs_id): + model_query(models.HostFs, read_deleted="no", + session=session).\ + filter_by(uuid=fs_id).\ + delete() + else: + model_query(models.HostFs, read_deleted="no").\ + filter_by(id=fs_id).\ + delete() diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/089_host_fs.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/089_host_fs.py new file mode 100644 index 0000000000..d57698987b --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/089_host_fs.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from sqlalchemy import DateTime, String, Integer +from sqlalchemy import Column, MetaData, Table, ForeignKey + +from sysinv.openstack.common import log + +ENGINE = 'InnoDB' +CHARSET = 'utf8' + +LOG = log.getLogger(__name__) + + +def upgrade(migrate_engine): + """ + This database upgrade creates a new host_fs table for storing + filesystem info for a host. + """ + + meta = MetaData() + meta.bind = migrate_engine + + Table('i_host', + meta, + Column('id', Integer, primary_key=True, nullable=False), + mysql_engine=ENGINE, mysql_charset=CHARSET, autoload=True) + + # Define and create the host_fs table. + fs_app = Table( + 'host_fs', + meta, + Column('created_at', DateTime), + Column('updated_at', DateTime), + Column('deleted_at', DateTime), + + Column('id', Integer, primary_key=True, nullable=False), + Column('uuid', String(36), unique=True), + Column('name', String(255)), + Column('size', Integer), + Column('logical_volume', String(64)), + Column('forihostid', Integer, + ForeignKey('i_host.id', ondelete='CASCADE')), + + mysql_engine=ENGINE, + mysql_charset=CHARSET, + ) + + fs_app.create() + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + # 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 d59c0c16a5..2b5241650b 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py @@ -15,7 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -1721,3 +1721,17 @@ class KubeAppReleases(Base): app_id = Column(Integer, ForeignKey('kube_app.id', ondelete='CASCADE')) kube_app = relationship("KubeApp", lazy="joined", join_depth=1) UniqueConstraint('release', 'namespace', 'app_id', name='u_app_release_namespace') + + +class HostFs(Base): + __tablename__ = 'host_fs' + + id = Column(Integer, primary_key=True) + uuid = Column(String(36)) + + name = Column(String(64)) + size = Column(Integer) + logical_volume = Column(String(64)) + 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/__init__.py b/sysinv/sysinv/sysinv/sysinv/objects/__init__.py index a7224c7ed3..b234e33ab1 100644 --- a/sysinv/sysinv/sysinv/sysinv/objects/__init__.py +++ b/sysinv/sysinv/sysinv/sysinv/objects/__init__.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # @@ -88,6 +88,7 @@ from sysinv.objects import storage_file from sysinv.objects import storage_external from sysinv.objects import storage_tier from sysinv.objects import storage_ceph_external +from sysinv.objects import host_fs def objectify(klass): @@ -185,6 +186,7 @@ label = label.Label kube_app = kube_app.KubeApp kube_app_releases = kube_app_releases.KubeAppReleases datanetwork = datanetwork.DataNetwork +host_fs = host_fs.HostFS __all__ = (system, cluster, @@ -254,6 +256,7 @@ __all__ = (system, kube_app_releases, datanetwork, interface_network, + host_fs, # alias objects for RPC compatibility ihost, ilvg, diff --git a/sysinv/sysinv/sysinv/sysinv/objects/host_fs.py b/sysinv/sysinv/sysinv/sysinv/objects/host_fs.py new file mode 100644 index 0000000000..e31d2a33a0 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/objects/host_fs.py @@ -0,0 +1,38 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# coding=utf-8 +# + +from sysinv.db import api as db_api +from sysinv.objects import base +from sysinv.objects import utils + + +class HostFS(base.SysinvObject): + + dbapi = db_api.get_instance() + + fields = { + 'id': int, + 'uuid': utils.str_or_none, + 'name': utils.str_or_none, + 'size': utils.int_or_none, + 'logical_volume': utils.str_or_none, + + 'forihostid': int, + 'ihost_uuid': utils.str_or_none, + } + + _foreign_fields = {'ihost_uuid': 'host:uuid'} + + @base.remotable_classmethod + def get_by_uuid(cls, context, uuid): + return cls.dbapi.host_fs_get(uuid) + + def save_changes(self, context, updates): + self.dbapi.host_fs_update(self.uuid, updates)