Enhancement for Controller Filesystem Extension

This commit adds following enhancement for controller filesystem
extension:

- Add a new column/row to show the size of available space in the
  volume group in host-lvg-list and host-lvg-show command output.
  This will help user know how much space is available to extend
  controller filesystem.
- Remove the 2G space reservation when calculating available space
  in cgts-vg volume group.
- Remove the unused code when calculating available space in cgts-vg
  volume group.
- Reduce the required cgts-vg space on standby controller by 2G when
  unlocking the standby controller for the first time.

Change-Id: Ic94a3544a9d02f67c592e07048b95135c2b350ed
Closes-Bug: 1804711
Signed-off-by: Wei Zhou <wei.zhou@windriver.com>
This commit is contained in:
Wei Zhou 2018-11-22 18:21:02 -05:00
parent b8bb709f18
commit 1655c79189
7 changed files with 66 additions and 154 deletions

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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': {},

View File

@ -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

View File

@ -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}