Merge "Support upgrades with additional partition types"

This commit is contained in:
Zuul 2021-11-25 20:37:08 +00:00 committed by Gerrit Code Review
commit d56a79fb0f
1 changed files with 77 additions and 100 deletions

View File

@ -5,18 +5,18 @@
# #
import math import math
import operator
import psycopg2 import psycopg2
import sys import sys
import subprocess import subprocess
from sysinv.common import constants
from psycopg2.extras import RealDictCursor
from controllerconfig.common import log from controllerconfig.common import log
from operator import itemgetter
from psycopg2.extras import RealDictCursor
LOG = log.get_logger(__name__) LOG = log.get_logger(__name__)
BACKUP_GUID = 'ba5eba11-0000-1111-2222-000000000002' BACKUP_GUID = 'ba5eba11-0000-1111-2222-000000000002'
SYSINV_GUID = 'ba5eba11-0000-1111-2222-000000000001'
def main(): def main():
@ -50,6 +50,7 @@ def main():
def adjust_backup_partition(): def adjust_backup_partition():
installed_backup_size = get_backup_size()
conn = psycopg2.connect("dbname=sysinv user=postgres") conn = psycopg2.connect("dbname=sysinv user=postgres")
with conn: with conn:
with conn.cursor(cursor_factory=RealDictCursor) as cur: with conn.cursor(cursor_factory=RealDictCursor) as cur:
@ -66,18 +67,9 @@ def adjust_backup_partition():
cur, controller, controller_rootfs_disk) cur, controller, controller_rootfs_disk)
LOG.info("Database partition data: %s" % db_partitions) LOG.info("Database partition data: %s" % db_partitions)
installed_partitions = get_partitions(
controller_rootfs_disk['device_path'],
controller_rootfs_disk['device_node'])
installed_partition_map = {
p['device_node']: p for p in installed_partitions}
LOG.info("Installed partitions: %s" % installed_partitions)
backup_partition = next(p for p in db_partitions if backup_partition = next(p for p in db_partitions if
p['type_guid'].lower() == BACKUP_GUID) p['type_guid'].lower() == BACKUP_GUID)
backup_device_node = backup_partition['device_node']
original_backup_size = backup_partition['size_mib'] original_backup_size = backup_partition['size_mib']
installed_backup_size = int(installed_partition_map[backup_device_node]['size_mib']) # noqa: E501
if installed_backup_size == original_backup_size: if installed_backup_size == original_backup_size:
LOG.info("Backup partition size unchanged, nothing to do. " LOG.info("Backup partition size unchanged, nothing to do. "
"Installed: %s DB: %s" % "Installed: %s DB: %s" %
@ -89,44 +81,45 @@ def adjust_backup_partition():
db_partitions, backup_partition, backup_change) db_partitions, backup_partition, backup_change)
# Ensure the last partition will fit on the disk # Ensure the last partition will fit on the disk
disk_size = get_disk_size(controller_rootfs_disk['device_node']) - 1 # noqa: E501 disk_size = controller_rootfs_disk['size_mib'] - 1
last_partition = adjusted_partitions[-1] last_partition = adjusted_partitions[-1]
required_space = max(0, last_partition['end_mib'] - disk_size) required_space = max(0, last_partition['end_mib'] - disk_size)
if required_space > 0:
LOG.info("Reducing partition: %s by %s" %
(last_partition['device_node'], required_space))
last_partition['end_mib'] -= required_space
last_partition['size_mib'] -= required_space
if last_partition['size_mib'] < 0:
raise Exception("Invalid partition configuration. Partitions: %s" % adjusted_partitions) # noqa: E501
update_partitions(cur, adjusted_partitions)
if required_space == 0: if required_space == 0:
update_partitions(cur, adjusted_partitions)
LOG.info("Adjusted partitions fit rootfs, can continue. " LOG.info("Adjusted partitions fit rootfs, can continue. "
"Partitions: %s " % adjusted_partitions) "Partitions: %s " % adjusted_partitions)
continue continue
cgts_vg = get_cgts_vg(cur, controller) added_partitions = [p for p in db_partitions if
cgts_vg_free_space = int(cgts_vg['lvm_vg_size'] / cgts_vg['lvm_vg_total_pe']) * cgts_vg['lvm_vg_free_pe'] # noqa: E501 p['type_guid'].lower() == SYSINV_GUID]
unassigned_partitions = [p for p in added_partitions if
p['foripvid'] is None]
# There may be available space in the cgts_vg if not added_partitions:
if cgts_vg_free_space >= required_space: # This is not an AIO system, we'll resize the last partiton
LOG.info("cgts_vg has sufficient space, can continue. " added_partitions.append(last_partition)
"cgts_vg: %s " % cgts_vg)
continue
# Otherwise we'll reduce the backup fs by up to 15GB and remove partitions = unassigned_partitions if unassigned_partitions else added_partitions # noqa
# the rest from the docker fs partition = max(partitions, key=itemgetter('size_mib'))
required_space -= cgts_vg_free_space
required_gb = int(math.ceil(required_space / 1024.0))
backup_fs_reduction = min(15, required_gb)
update_host_fs(cur, controller, 'backup', backup_fs_reduction)
required_gb -= backup_fs_reduction if partition['size_mib'] < required_space:
if required_gb > 0: LOG.exception(
update_host_fs(cur, controller, 'docker', required_gb) "Insufficient space to resize partition %s - %s" %
(partition, required_space))
raise
reduced_partitions = move_partitions(
adjusted_partitions, partition, required_space * -1)
final_partitions = adjusted_partitions[:adjusted_partitions.index(partition)] # noqa
final_partitions.extend(reduced_partitions)
update_partitions(cur, final_partitions)
host_pvs = get_pvs(cur, controller)
partition_vg_name = get_vg_name(partition, host_pvs)
if partition_vg_name == 'cgts-vg':
resize_cgts_vg(cur, controller, required_space)
def get_host_rootfs(cursor, host): def get_host_rootfs(cursor, host):
@ -141,34 +134,16 @@ def get_db_partitions(cursor, host, rootfs):
return cursor.fetchall() return cursor.fetchall()
def get_partitions(device_path, device_node): def get_backup_size():
"""Obtain existing partitions from a disk.""" lsblk_command = 'lsblk -pno PKNAME $(findmnt -n / -o SOURCE)'
partitions = [] lsblk = subprocess.Popen(lsblk_command, stdout=subprocess.PIPE, shell=True)
sgdisk_part_info = get_sgdisk_info(device_path) root_disk_path = lsblk.stdout.read()
part_info = get_sgdisk_info(root_disk_path)
for partition in sgdisk_part_info: backup_size = next(part['size_mib'] for part in part_info if
partition_number = partition.get('part_number') part['type_guid'].lower() == BACKUP_GUID)
type_name = partition.get('type_name')
part_size_mib = partition.get('size_mib')
part_device_node = build_partition_device_node(
device_node, partition_number)
part_device_path = build_partition_device_path(
device_path, partition_number)
start_mib = partition.get('start_mib')
end_mib = partition.get('end_mib')
part_attrs = { return int(backup_size)
'partition_number': partition_number,
'device_path': part_device_path,
'device_node': part_device_node,
'type_name': type_name,
'start_mib': start_mib,
'end_mib': end_mib,
'size_mib': part_size_mib,
}
partitions.append(part_attrs)
return partitions
def get_sgdisk_info(device_path): def get_sgdisk_info(device_path):
@ -206,40 +181,17 @@ def get_sgdisk_info(device_path):
return sgdisk_part_info return sgdisk_part_info
def build_partition_device_node(disk_device_node, partition_number):
if constants.DEVICE_NAME_NVME in disk_device_node:
partition_device_node = '{}p{}'.format(
disk_device_node, partition_number)
else:
partition_device_node = '{}{}'.format(
disk_device_node, partition_number)
LOG.debug("partition_device_node: %s" % partition_device_node)
return partition_device_node
def build_partition_device_path(disk_device_path, partition_number):
partition_device_path = '{}-part{}'.format(
disk_device_path, partition_number)
LOG.debug("partition_device_path: %s" % partition_device_path)
return partition_device_path
def move_partitions(db_values, start, size): def move_partitions(db_values, start, size):
""" """
Updates the list of partitions based on the new size of the platform backup Updates the list of partitions based on the new size of a given partition
partition
:param: db_values: A list of partitions to adjust :param: db_values: A list of partitions to adjust
:param: start: The platform-backup partition :param: start: The partition being adjusted
:param: size: The new size of the platform-backup partition :param: size: The change in size of the partition
:returns: A sorted list of updated partitions :returns: A sorted list of updated partitions
""" """
partitions = sorted(db_values, key=operator.itemgetter('start_mib')) partitions = sorted(db_values, key=itemgetter('start_mib'))
partitions = partitions[partitions.index(start):] partitions = partitions[partitions.index(start):]
# Update the platform backup size and end_mib # Update the specified partition size and end_mib
partitions[0]['size_mib'] += size partitions[0]['size_mib'] += size
partitions[0]['end_mib'] += size partitions[0]['end_mib'] += size
# Shift the rest of the partitions # Shift the rest of the partitions
@ -259,14 +211,39 @@ def update_partitions(cursor, updated_partitions):
partition['size_mib'], partition['id'])) partition['size_mib'], partition['id']))
def get_disk_size(rootfs): def get_pvs(cursor, host):
disk_size_cmd = '{} {}'.format('blockdev --getsize64', rootfs) query = "select * from i_pv where forihostid=%s"
disk_size_process = subprocess.Popen( cursor.execute(query, (host['id'],))
disk_size_cmd, stdout=subprocess.PIPE, shell=True) return cursor.fetchall()
disk_size = int(disk_size_process.stdout.read().rstrip())
# Return in mib
return int(disk_size / (1024 * 1024)) def get_vg_name(partition, pvs):
pv_id = partition['foripvid']
if not pv_id:
return None
return next(pv['lvm_vg_name'] for pv in pvs if pv['id'] == pv_id)
def resize_cgts_vg(cursor, host, required_space):
cgts_vg = get_cgts_vg(cursor, host)
cgts_vg_free_space = int(cgts_vg['lvm_vg_size'] / cgts_vg['lvm_vg_total_pe']) * cgts_vg['lvm_vg_free_pe'] # noqa: E501
# There may be available space in the cgts_vg
if cgts_vg_free_space >= required_space:
LOG.info("cgts_vg has sufficient space, can continue. "
"cgts_vg: %s " % cgts_vg)
return
# Otherwise we'll reduce the backup fs by up to 15GB and remove
# the rest from the docker fs
required_space -= cgts_vg_free_space
required_gb = int(math.ceil(required_space / 1024.0))
backup_fs_reduction = min(15, required_gb)
update_host_fs(cursor, host, 'backup', backup_fs_reduction)
required_gb -= backup_fs_reduction
if required_gb > 0:
update_host_fs(cursor, host, 'docker', required_gb)
def get_cgts_vg(cursor, host): def get_cgts_vg(cursor, host):