diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/common/utils.py b/sysinv/cgts-client/cgts-client/cgtsclient/common/utils.py index 62763a311a..e4f1ee810a 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/common/utils.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/common/utils.py @@ -23,6 +23,7 @@ except Exception: import argparse import copy import dateutil +import math import os import prettytable import re @@ -749,10 +750,18 @@ def extract_keypairs(args): return attributes -# Convert size from BYTE to KiB, MiB, GiB, TiB, PiB -# 1 - KiB, 2 - MiB, 3 - GiB, 4 - TiB, 5 - PiB -def convert_size_from_bytes(bytes, type): - return '%.2f' % (float(bytes) / (1024 ** type)) +def size_unit_conversion(size, step): + """ + This function converts size from a smaller unit (e.g. KiB) + to a larger unit (e.g. GiB). + + :param size: Size value to convert from one unit to another + :param step: Power of 2^10. e.g. From Byte to MiB is 2 steps. + From MiB to GiB is 1 step. + :returns: The return value is a float with 3 digits after + the decimal point. + """ + return math.floor(float(size) / (1024 ** step) * 1000) / 1000.0 def _get_system_info(cc): diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/ilvg_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/ilvg_shell.py index ff69b1d8c2..f78c071b94 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/ilvg_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/ilvg_shell.py @@ -9,7 +9,6 @@ # All Rights Reserved. # -from cgtsclient.common import constants from cgtsclient.common import utils from cgtsclient import exc from cgtsclient.v1 import ihost as ihost_utils @@ -20,17 +19,20 @@ from oslo_serialization import jsonutils def _print_ilvg_show(ilvg): labels = ['lvm_vg_name', 'vg_state', 'uuid', 'ihost_uuid', 'lvm_vg_access', 'lvm_max_lv', 'lvm_cur_lv', 'lvm_max_pv', 'lvm_cur_pv', - 'lvm_vg_size_gib', 'lvm_vg_total_pe', 'lvm_vg_free_pe', 'created_at', - 'updated_at', 'parameters'] + 'lvm_vg_size_gib', 'lvm_vg_avail_size_gib', 'lvm_vg_total_pe', + 'lvm_vg_free_pe', 'created_at', 'updated_at', 'parameters'] fields = ['lvm_vg_name', 'vg_state', 'uuid', 'ihost_uuid', 'lvm_vg_access', 'lvm_max_lv', 'lvm_cur_lv', 'lvm_max_pv', 'lvm_cur_pv', - 'lvm_vg_size', 'lvm_vg_total_pe', 'lvm_vg_free_pe', 'created_at', - 'updated_at'] + 'lvm_vg_size', 'lvm_vg_avail_size', 'lvm_vg_total_pe', + 'lvm_vg_free_pe', 'created_at', 'updated_at'] # convert size from Byte to GiB - ilvg.lvm_vg_size = utils.convert_size_from_bytes(ilvg.lvm_vg_size, - constants.GiB) + ilvg.lvm_vg_avail_size = \ + utils.size_unit_conversion(ilvg.lvm_vg_avail_size, 3) + + # convert size from Byte to GiB + ilvg.lvm_vg_size = utils.size_unit_conversion(ilvg.lvm_vg_size, 3) data = [(f, getattr(ilvg, f, '')) for f in fields] @@ -95,13 +97,18 @@ def do_host_lvg_list(cc, args): lvg.vg_state = _adjust_state_data(lvg.lvm_vg_name, lvg.vg_state) # convert size from Byte to GiB - lvg.lvm_vg_size = utils.convert_size_from_bytes(lvg.lvm_vg_size, - constants.GiB) + lvg.lvm_vg_avail_size = \ + utils.size_unit_conversion(lvg.lvm_vg_avail_size, 3) + + # convert size from Byte to GiB + lvg.lvm_vg_size = \ + utils.size_unit_conversion(lvg.lvm_vg_size, 3) field_labels = ['UUID', 'LVG Name', 'State', 'Access', - 'Size (GiB)', 'Current PVs', 'Current LVs'] + 'Total Size (GiB)', 'Avail Size (GiB)', + 'Current PVs', 'Current LVs'] fields = ['uuid', 'lvm_vg_name', 'vg_state', 'lvm_vg_access', - 'lvm_vg_size', 'lvm_cur_pv', 'lvm_cur_lv'] + 'lvm_vg_size', 'lvm_vg_avail_size', 'lvm_cur_pv', 'lvm_cur_lv'] utils.print_list(ilvgs, fields, field_labels, sortby=0) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/controller_fs.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/controller_fs.py index 17d104d67c..2b453e4c80 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/controller_fs.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/controller_fs.py @@ -253,22 +253,10 @@ def _check_controller_multi_fs(controller_fs_new_list, LOG.info("_check_controller__multi_fs ceph_mon_gib_new = %s" % ceph_mon_gib_new) - device_path_ctrl0 = None - device_path_ctrl1 = None + cgtsvg_max_free_GiB = _get_controller_cgtsvg_limit() - if ceph_mons: - for ceph_mon in ceph_mons: - ihost = pecan.request.dbapi.ihost_get(ceph_mon.forihostid) - if ihost.hostname == constants.CONTROLLER_0_HOSTNAME: - device_path_ctrl0 = ceph_mon.device_path - if ihost.hostname == constants.CONTROLLER_1_HOSTNAME: - device_path_ctrl1 = ceph_mon.device_path - - rootfs_max_GiB, cgtsvg_max_free_GiB = \ - _get_controller_fs_limit(device_path_ctrl0, device_path_ctrl1) - - LOG.info("_check_controller_multi_fs rootfs_max_GiB = %s cgtsvg_max_free_GiB = %s " % - (rootfs_max_GiB, cgtsvg_max_free_GiB)) + LOG.info("_check_controller_multi_fs cgtsvg_max_free_GiB = %s " % + cgtsvg_max_free_GiB) _check_relative_controller_multi_fs(controller_fs_new_list) @@ -392,19 +380,11 @@ def _check_controller_state(): return True -def _get_controller_fs_limit(device_path_ctrl0, device_path_ctrl1): - """Calculate space for controller rootfs plus ceph_mon_dev - returns: fs_max_GiB - cgtsvg_max_free_GiB +def _get_controller_cgtsvg_limit(): + """Calculate space for controller fs + returns: cgtsvg_max_free_GiB """ - reserved_space = constants.CONTROLLER_ROOTFS_RESERVED - - max_disk_size_controller0 = 0 - max_disk_size_controller1 = 0 - - idisks0 = None - idisks1 = None cgtsvg0_free_mib = 0 cgtsvg1_free_mib = 0 cgtsvg_max_free_GiB = 0 @@ -413,8 +393,6 @@ def _get_controller_fs_limit(device_path_ctrl0, device_path_ctrl1): constants.CONTROLLER) for chost in chosts: if chost.hostname == constants.CONTROLLER_0_HOSTNAME: - idisks0 = pecan.request.dbapi.idisk_get_by_ihost(chost.uuid) - ipvs = pecan.request.dbapi.ipv_get_by_ihost(chost.uuid) for ipv in ipvs: if (ipv.lvm_vg_name == constants.LVG_CGTS_VG and @@ -433,8 +411,6 @@ def _get_controller_fs_limit(device_path_ctrl0, device_path_ctrl1): break else: - idisks1 = pecan.request.dbapi.idisk_get_by_ihost(chost.uuid) - ipvs = pecan.request.dbapi.ipv_get_by_ihost(chost.uuid) for ipv in ipvs: if (ipv.lvm_vg_name == constants.LVG_CGTS_VG and @@ -452,59 +428,9 @@ def _get_controller_fs_limit(device_path_ctrl0, device_path_ctrl1): ilvg.lvm_vg_total_pe)) / (1024 * 1024) break - LOG.info("_get_controller_fs_limit cgtsvg0_free_mib=%s, " + LOG.info("_get_controller_cgtsvg_limit cgtsvg0_free_mib=%s, " "cgtsvg1_free_mib=%s" % (cgtsvg0_free_mib, cgtsvg1_free_mib)) - # relies on the sizes of the partitions allocated in - # cgcs/common-bsp/files/TEMPLATE_controller_disk.add. - - for chost in chosts: - if chost.hostname == constants.CONTROLLER_0_HOSTNAME and idisks0: - idisks = idisks0 - elif chost.hostname == constants.CONTROLLER_1_HOSTNAME and idisks1: - idisks = idisks1 - else: - LOG.error("SYS_I unexpected chost uuid %s hostname %s" % - (chost.uuid, chost.hostname)) - continue - - # find the largest disk for each controller - for idisk in idisks: - capabilities = idisk['capabilities'] - if 'stor_function' in capabilities: - if capabilities['stor_function'] == 'rootfs': - disk_size_gib = idisk.size_mib / 1024 - if chost.hostname == constants.CONTROLLER_0_HOSTNAME: - if disk_size_gib > max_disk_size_controller0: - max_disk_size_controller0 = disk_size_gib - else: - if disk_size_gib > max_disk_size_controller1: - max_disk_size_controller1 = disk_size_gib - - if (device_path_ctrl0 == idisk.device_path and - chost.hostname == constants.CONTROLLER_0_HOSTNAME): - disk_size_gib = idisk.size_mib / 1024 - max_disk_size_controller0 += disk_size_gib - - elif (device_path_ctrl1 == idisk.device_path and - chost.hostname == constants.CONTROLLER_1_HOSTNAME): - disk_size_gib = idisk.size_mib / 1024 - max_disk_size_controller1 += disk_size_gib - - if max_disk_size_controller0 > 0 and max_disk_size_controller1 > 0: - minimax = min(max_disk_size_controller0, max_disk_size_controller1) - LOG.info("_get_controller_fs_limit minimax=%s" % minimax) - fs_max_GiB = minimax - reserved_space - elif max_disk_size_controller1 > 0: - fs_max_GiB = max_disk_size_controller1 - reserved_space - else: - fs_max_GiB = max_disk_size_controller0 - reserved_space - - LOG.info("SYS_I filesystem limits max_disk_size_controller0=%s, " - "max_disk_size_controller1=%s, reserved_space=%s, fs_max_GiB=%s" % - (max_disk_size_controller0, max_disk_size_controller1, - reserved_space, int(fs_max_GiB))) - if cgtsvg0_free_mib > 0 and cgtsvg1_free_mib > 0: cgtsvg_max_free_GiB = min(cgtsvg0_free_mib, cgtsvg1_free_mib) / 1024 LOG.info("min of cgtsvg0_free_mib=%s and cgtsvg1_free_mib=%s is " @@ -515,37 +441,11 @@ def _get_controller_fs_limit(device_path_ctrl0, device_path_ctrl1): else: cgtsvg_max_free_GiB = cgtsvg0_free_mib / 1024 - cgtsvg_max_free_GiB -= constants.CFS_RESIZE_BUFFER_GIB - LOG.info("SYS_I filesystem limits cgtsvg0_free_mib=%s, " "cgtsvg1_free_mib=%s, cgtsvg_max_free_GiB=%s" % (cgtsvg0_free_mib, cgtsvg1_free_mib, cgtsvg_max_free_GiB)) - return fs_max_GiB, cgtsvg_max_free_GiB - - -def get_controller_fs_limit(): - ceph_mons = pecan.request.dbapi.ceph_mon_get_list() - - if ceph_mons: - ceph_mon_gib_new = ceph_mons[0].ceph_mon_gib - else: - ceph_mon_gib_new = 0 - - LOG.debug("_check_controller_fs ceph_mon_gib_new = %s" % ceph_mon_gib_new) - - device_path_ctrl0 = None - device_path_ctrl1 = None - - if ceph_mons: - for ceph_mon in ceph_mons: - ihost = pecan.request.dbapi.ihost_get(ceph_mon.forihostid) - if ihost.hostname == constants.CONTROLLER_0_HOSTNAME: - device_path_ctrl0 = ceph_mon.device_path - if ihost.hostname == constants.CONTROLLER_1_HOSTNAME: - device_path_ctrl1 = ceph_mon.device_path - - return _get_controller_fs_limit(device_path_ctrl0, device_path_ctrl1) + return cgtsvg_max_free_GiB def _check_controller_fs(controller_fs_new=None, @@ -569,23 +469,10 @@ def _check_controller_fs(controller_fs_new=None, else: cgtsvg_growth_gib = ceph_mon_gib_new - device_path_ctrl0 = None - device_path_ctrl1 = None - - if ceph_mons: - for ceph_mon in ceph_mons: - ihost = pecan.request.dbapi.ihost_get(ceph_mon.forihostid) - if ihost.hostname == constants.CONTROLLER_0_HOSTNAME: - device_path_ctrl0 = ceph_mon.device_path - if ihost.hostname == constants.CONTROLLER_1_HOSTNAME: - device_path_ctrl1 = ceph_mon.device_path - - rootfs_max_GiB, cgtsvg_max_free_GiB = \ - _get_controller_fs_limit(device_path_ctrl0, device_path_ctrl1) + cgtsvg_max_free_GiB = _get_controller_cgtsvg_limit() LOG.info("_check_controller_fs ceph_mon_gib_new = %s" % ceph_mon_gib_new) LOG.info("_check_controller_fs cgtsvg_growth_gib = %s" % cgtsvg_growth_gib) - LOG.info("_check_controller_fs rootfs_max_GiB = %s" % rootfs_max_GiB) LOG.info("_check_controller_fs cgtsvg_max_free_GiB = %s" % cgtsvg_max_free_GiB) _check_relative_controller_fs(controller_fs_new, controller_fs_list) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py index 0b6f77ee7c..27c4d413e8 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py @@ -23,6 +23,7 @@ import ast import cgi import copy import json +import math import os import re import xml.etree.ElementTree as ET @@ -3872,17 +3873,13 @@ class HostController(rest.RestController): str(standby_controller_allocated_space)) if (active_controller_used > standby_controller_allocated_space): - # Since we allocate space that is measured in GiB, the human - # readable information shown in case of an error should also - # be in GiB. We add a 2GB buffer (the same used when changing - # filesystem sizes) to ensure no rounding errors - needed_space = (float( + # Round up the needed space from float to integer + needed_space = math.ceil(float( active_controller_used - - standby_controller_allocated_space) / (1024 ** 3) + - constants.CFS_RESIZE_BUFFER_GIB) + standby_controller_allocated_space) / (1024 ** 3)) msg = _("Standby controller does not have enough space allocated to " "%(vg_name)s volume-group in order to create all filesystems. " - "Please assign an extra %(needed).2f GB to the volume group.") % { + "Please assign an extra %(needed)d GB to the volume group.") % { 'vg_name': constants.LVG_CGTS_VG, 'needed': needed_space} raise wsme.exc.ClientSideError(msg) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/lvg.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/lvg.py index 4ae9681b34..2bfa49ed2b 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/lvg.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/lvg.py @@ -92,7 +92,10 @@ class LVG(base.APIBase): "LVM Volume Group's current physical volumes" lvm_vg_size = int - "LVM Volume Group's size" + "LVM Volume Group's total size" + + lvm_vg_avail_size = int + "LVM Volume Group's available size. API only attribute" lvm_vg_total_pe = int "LVM Volume Group's total PEs" @@ -124,6 +127,9 @@ class LVG(base.APIBase): if not self.uuid: self.uuid = uuidutils.generate_uuid() + self.fields.append('lvm_vg_avail_size') + setattr(self, 'lvm_vg_avail_size', kwargs.get('lvm_vg_avail_size', 0)) + @classmethod def convert_with_links(cls, rpc_lvg, expand=True): lvg = LVG(**rpc_lvg.as_dict()) @@ -132,11 +138,22 @@ class LVG(base.APIBase): 'lvm_vg_uuid', 'lvm_vg_access', 'lvm_max_lv', 'lvm_cur_lv', 'lvm_max_pv', 'lvm_cur_pv', - 'lvm_vg_size', 'lvm_vg_total_pe', + 'lvm_vg_size', 'lvm_vg_avail_size', + 'lvm_vg_total_pe', 'lvm_vg_free_pe', 'capabilities', 'created_at', 'updated_at', 'ihost_uuid', 'forihostid']) + # To calculate Volume Group's available size in byte: + # lvm_vg_size is Volume Group's total size in byte + # lvm_vg_free_pe is Volume Group's free Physical Extents + # lvm_vg_total_pe is Volume Group's total Physical Extents + if lvg.lvm_vg_total_pe > 0: + lvg.lvm_vg_avail_size = \ + lvg.lvm_vg_size * lvg.lvm_vg_free_pe / lvg.lvm_vg_total_pe + else: + lvg.lvm_vg_avail_size = 0 + # never expose the ihost_id attribute, allow exposure for now lvg.forihostid = wtypes.Unset lvg.links = [link.Link.make_link('self', pecan.request.host_url, @@ -490,6 +507,7 @@ def _set_defaults(lvg): 'lvm_max_pv': 0, 'lvm_cur_pv': 0, 'lvm_vg_size': 0, + 'lvm_vg_avail_size': 0, 'lvm_vg_total_pe': 0, 'lvm_vg_free_pe': 0, 'capabilities': {}, diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index b3e6e5b8fd..059cf3ffde 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -435,12 +435,6 @@ SB_CONFIGURATION_TIMEOUT = 1200 # Storage: Minimum number of monitors MIN_STOR_MONITORS = 2 -# Storage: reserved space for calculating controller rootfs limit -CONTROLLER_ROOTFS_RESERVED = 38 - -# Controller filesystem reserved space to ensure no rounding errors -CFS_RESIZE_BUFFER_GIB = 2 - BACKUP_OVERHEAD = 20 # Suffix used in LVM volume name to indicate that the diff --git a/sysinv/sysinv/sysinv/sysinv/common/storage_backend_conf.py b/sysinv/sysinv/sysinv/sysinv/common/storage_backend_conf.py index e2a20d0c76..1c7f664416 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/storage_backend_conf.py +++ b/sysinv/sysinv/sysinv/sysinv/common/storage_backend_conf.py @@ -387,7 +387,7 @@ class StorageBackendConfig(object): return # Check if there is enough space available - rootfs_max_GiB, cgtsvg_max_free_GiB = controller_fs_api.get_controller_fs_limit() + cgtsvg_max_free_GiB = controller_fs_api._get_controller_cgtsvg_limit() args = {'avail': cgtsvg_max_free_GiB, 'min': constants.DEFAULT_SMALL_IMG_CONVERSION_STOR_SIZE, 'lvg': constants.LVG_CGTS_VG}