Upgrades: Add AIO partition data migration
In STX 4.0 the /scratch partition increased from 8G to 16G. This increases the size of primary Linux LVM partition created by Anaconda as compared to the size provided in STX 3.0. To support the upgrade path from STX 3.0 to STX 4.0 we must migrate the sysinv partition information for the primary Linux LVM partition and any user created partitions on the root disk. This migration allows the new partition layout on disk to match what is expected in the sysinv database and will create any missing user defined partitions on system unlock. Change-Id: I060e7055d524d4ae44b595b9a172752aa5ac77ae Closes-Bug: #1887192 Signed-off-by: Robert Church <robert.church@windriver.com>
This commit is contained in:
parent
953ae15c00
commit
c3c561e811
280
controllerconfig/controllerconfig/upgrade-scripts/10-sysinv-adjust-partitions.py
Executable file
280
controllerconfig/controllerconfig/upgrade-scripts/10-sysinv-adjust-partitions.py
Executable file
|
@ -0,0 +1,280 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright (c) 2020 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script will update the partition schema for partitions on the root disk
|
||||
# of AIO controllers. This is required as the default LVM partiton grew in the
|
||||
# N+1 release.
|
||||
|
||||
import psycopg2
|
||||
import sys
|
||||
import six
|
||||
import subprocess
|
||||
|
||||
from sysinv.common import constants
|
||||
from psycopg2.extras import RealDictCursor
|
||||
from controllerconfig.common import log
|
||||
|
||||
LOG = log.get_logger(__name__)
|
||||
|
||||
|
||||
def main():
|
||||
action = None
|
||||
from_release = None
|
||||
to_release = None # noqa
|
||||
arg = 1
|
||||
|
||||
while arg < len(sys.argv):
|
||||
if arg == 1:
|
||||
from_release = sys.argv[arg]
|
||||
elif arg == 2:
|
||||
to_release = sys.argv[arg] # noqa
|
||||
elif arg == 3:
|
||||
action = sys.argv[arg]
|
||||
else:
|
||||
print ("Invalid option %s." % sys.argv[arg])
|
||||
return 1
|
||||
arg += 1
|
||||
|
||||
log.configure()
|
||||
|
||||
LOG.debug("%s invoked with from_release = %s to_release = %s action = %s"
|
||||
% (sys.argv[0], from_release, to_release, action))
|
||||
if to_release == "20.06" and action == "migrate":
|
||||
try:
|
||||
adjust_user_partitions()
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
return 1
|
||||
|
||||
|
||||
def _command(arguments1, arguments2=None):
|
||||
"""Execute a command and capture stdout, stderr & return code."""
|
||||
LOG.debug("Executing command: '%s'" % " ".join(arguments1))
|
||||
process = subprocess.Popen(
|
||||
arguments1,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
if arguments2:
|
||||
process2 = subprocess.Popen(
|
||||
arguments2,
|
||||
stdin=process.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
shell=False)
|
||||
process.stdout.close()
|
||||
process = process2
|
||||
|
||||
out, err = process.communicate()
|
||||
|
||||
return out, err, process.returncode
|
||||
|
||||
|
||||
def get_sgdisk_info(device_path):
|
||||
"""Obtain partition info: type GUID, type name, UUID, start, end, size.
|
||||
:param: device_path: the disk's device path
|
||||
:returns: list of partition info
|
||||
"""
|
||||
sgdisk_part_info = []
|
||||
fields = ['part_number', 'device_node', 'type_guid', 'type_name', 'uuid',
|
||||
'start_mib', 'end_mib', 'size_mib']
|
||||
sgdisk_command = '{} {}'.format('/usr/bin/partition_info.sh',
|
||||
device_path)
|
||||
|
||||
try:
|
||||
sgdisk_process = subprocess.Popen(sgdisk_command,
|
||||
stdout=subprocess.PIPE,
|
||||
shell=True)
|
||||
except Exception as e:
|
||||
LOG.exception("Could not retrieve partition information: %s" % e)
|
||||
raise
|
||||
|
||||
sgdisk_output = sgdisk_process.stdout.read()
|
||||
|
||||
rows = [row for row in sgdisk_output.split(';') if row.strip()]
|
||||
|
||||
for row in rows:
|
||||
values = row.split()
|
||||
partition = dict(zip(fields, values))
|
||||
|
||||
if 'part_number' in partition.keys():
|
||||
partition['part_number'] = int(partition['part_number'])
|
||||
|
||||
sgdisk_part_info.append(partition)
|
||||
|
||||
return sgdisk_part_info
|
||||
|
||||
|
||||
def get_partitions(device_path, device_node):
|
||||
"""Obtain existing partitions from a disk."""
|
||||
partitions = []
|
||||
sgdisk_part_info = get_sgdisk_info(device_path)
|
||||
|
||||
for partition in sgdisk_part_info:
|
||||
partition_number = partition.get('part_number')
|
||||
type_name = partition.get('type_name')
|
||||
part_size_mib = partition.get('size_mib')
|
||||
if constants.DEVICE_NAME_NVME in device_node:
|
||||
part_device_node = '{}p{}'.format(device_node, partition_number)
|
||||
else:
|
||||
part_device_node = '{}{}'.format(device_node, partition_number)
|
||||
part_device_path = '{}-part{}'.format(device_path,
|
||||
partition_number)
|
||||
start_mib = partition.get('start_mib')
|
||||
end_mib = partition.get('end_mib')
|
||||
|
||||
part_attrs = {
|
||||
'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 is_aio_system_type():
|
||||
conn = psycopg2.connect("dbname='sysinv' user='postgres'")
|
||||
with conn:
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
cur.execute("SELECT * from i_system")
|
||||
system = cur.fetchone()
|
||||
return system['system_type'] == 'All-in-one'
|
||||
|
||||
|
||||
def adjust_user_partitions():
|
||||
if not is_aio_system_type:
|
||||
LOG.info("This is not an AIO system. No partition changes required.")
|
||||
return
|
||||
|
||||
conn = psycopg2.connect("dbname=sysinv user=postgres")
|
||||
with conn:
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
cur.execute("select i_host.id, i_host.rootfs_device from i_host "
|
||||
"where personality='controller'")
|
||||
controllers = cur.fetchall()
|
||||
if not controllers:
|
||||
LOG.exception("Failed to fetch controller host information")
|
||||
raise
|
||||
|
||||
for controller in controllers:
|
||||
# Get the root disk for the controller.
|
||||
cur.execute(
|
||||
"select * from i_idisk where forihostid=%s and "
|
||||
"capabilities like %s", (controller['id'], '%rootfs%',))
|
||||
controller_rootfs_disk = cur.fetchone()
|
||||
if not controller_rootfs_disk:
|
||||
LOG.exception("Could not locate controller root disk.")
|
||||
raise
|
||||
LOG.debug("controller_rootfs_disk: %s" %
|
||||
controller_rootfs_disk)
|
||||
|
||||
# Get the partitions for the controller root disk.
|
||||
cur.execute(
|
||||
"select partition.id, partition.device_node, "
|
||||
"partition.device_path, partition.start_mib, "
|
||||
"partition.end_mib, partition.size_mib "
|
||||
"from partition where forihostid = %s",
|
||||
(controller['id'],))
|
||||
db_partitions = cur.fetchall()
|
||||
LOG.debug("DB partitions: %s" % db_partitions)
|
||||
|
||||
# Create a map
|
||||
partition_map = {p['device_node']: p for p in db_partitions}
|
||||
LOG.debug("DB partition map: %s" % partition_map)
|
||||
|
||||
installed_partitions = get_partitions(
|
||||
controller_rootfs_disk['device_path'],
|
||||
controller_rootfs_disk['device_node'])
|
||||
LOG.debug("installed partitions: %s" % installed_partitions)
|
||||
|
||||
update_db_partitions = [] # Requires DB updates
|
||||
installed_lvm_device = None # LVM device that needs adjusting
|
||||
adjustments = {} # LVM device partition adjustments
|
||||
|
||||
# Go through the installed partitions and determine any changes
|
||||
for i in installed_partitions:
|
||||
# Grab the partition from the db map
|
||||
d = partition_map[i['device_node']]
|
||||
if ((int(i['start_mib']) != int(d['start_mib'])) or
|
||||
(int(i['end_mib']) != int(d['end_mib'])) or
|
||||
(int(i['size_mib']) != int(d['size_mib']))):
|
||||
LOG.info("MISMATCH:installed part: %s %s %s %s" % (
|
||||
i['device_node'], i['start_mib'],
|
||||
i['end_mib'], i['size_mib']))
|
||||
LOG.info("MISMATCH: db part: %s %s %s %s" % (
|
||||
d['device_node'], d['start_mib'],
|
||||
d['end_mib'], d['size_mib']))
|
||||
if i['type_name'] == 'Linux.LVM':
|
||||
# This is key partition that will be used to adjust
|
||||
# any additional user created partitions, identify
|
||||
# and save the adjustments
|
||||
installed_lvm_device = i['device_node']
|
||||
adjustments['start_mib'] = (int(i['start_mib']) -
|
||||
int(d['start_mib']))
|
||||
adjustments['end_mib'] = (int(i['end_mib']) -
|
||||
int(d['end_mib']))
|
||||
adjustments['size_mib'] = (int(i['size_mib']) -
|
||||
int(d['size_mib']))
|
||||
else:
|
||||
# Adjust the non-LVM partitions to match what is
|
||||
# installed
|
||||
d['start_mib'] = i['start_mib']
|
||||
d['end_mib'] = i['end_mib']
|
||||
d['size_mib'] = i['size_mib']
|
||||
|
||||
# Save the new partition for updating
|
||||
update_db_partitions.append(d)
|
||||
|
||||
# Remove the partition from the db map
|
||||
del partition_map[i['device_node']]
|
||||
else:
|
||||
# Partition is the same. No changes needed
|
||||
# Remove the partition from the db map
|
||||
del partition_map[i['device_node']]
|
||||
|
||||
if installed_lvm_device:
|
||||
# Found a difference in the installed partition map for the
|
||||
# primary LVM partition
|
||||
LOG.debug("DB unhandled part map: %s" % partition_map)
|
||||
|
||||
# Update the primary installed LVM partition based on
|
||||
# calculated adjustments.
|
||||
d = partition_map[installed_lvm_device]
|
||||
d['start_mib'] = (int(d['start_mib']) +
|
||||
adjustments['start_mib'])
|
||||
d['end_mib'] = (int(d['end_mib']) +
|
||||
adjustments['end_mib'])
|
||||
d['size_mib'] = (int(d['size_mib']) +
|
||||
adjustments['size_mib'])
|
||||
update_db_partitions.append(d)
|
||||
del partition_map[installed_lvm_device]
|
||||
|
||||
# Adjust the start/end of user created partitions. Size
|
||||
# will not be changed.
|
||||
for device, partition in six.iteritems(partition_map):
|
||||
partition['start_mib'] = (int(partition['start_mib']) +
|
||||
adjustments['end_mib'])
|
||||
partition['end_mib'] = (int(partition['end_mib']) +
|
||||
adjustments['end_mib'])
|
||||
update_db_partitions.append(partition)
|
||||
|
||||
if update_db_partitions:
|
||||
# Found partitions that need updating
|
||||
LOG.info("Required partition adjustments: %s" %
|
||||
update_db_partitions)
|
||||
for partition in update_db_partitions:
|
||||
cur.execute(
|
||||
"update partition set start_mib=%s, end_mib=%s, "
|
||||
"size_mib=%s where id=%s", (partition['start_mib'],
|
||||
partition['end_mib'],
|
||||
partition['size_mib'],
|
||||
partition['id']),)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Loading…
Reference in New Issue