Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tolerance of pre-existing software-RAID #6

Open
wants to merge 11 commits into
base: 10.10.11-8.3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# destinations
DESTDIR =
INSTALLER_DIR = /opt/xensource/installer
EFI_DIR = /EFI/xenserver

# root of a tree with sm.rpm unpacked
SM_ROOTDIR =

INSTALL = install

install:
$(INSTALL) -d $(DESTDIR)/usr/bin
$(INSTALL) -m755 support.sh $(DESTDIR)/usr/bin
$(INSTALL) -d $(DESTDIR)$(INSTALLER_DIR)/tui/installer/
$(INSTALL) -m755 \
init \
$(DESTDIR)$(INSTALLER_DIR)/
$(INSTALL) -m644 \
keymaps \
timezones \
answerfile.py \
backend.py \
common_criteria_firewall_rules \
constants.py \
cpiofile.py \
disktools.py \
diskutil.py \
driver.py \
fcoeutil.py \
generalui.py \
hardware.py \
init_constants.py \
install.py \
netinterface.py \
netutil.py \
product.py \
report.py \
repository.py \
restore.py \
scripts.py \
snackutil.py \
uicontroller.py \
upgrade.py \
util.py \
xelogging.py \
$(DESTDIR)$(INSTALLER_DIR)/
$(INSTALL) -m644 \
tui/__init__.py \
tui/init.py \
tui/fcoe.py \
tui/network.py \
tui/progress.py \
tui/repo.py \
$(DESTDIR)$(INSTALLER_DIR)/tui/
$(INSTALL) -m644 \
tui/installer/__init__.py \
tui/installer/screens.py \
$(DESTDIR)$(INSTALLER_DIR)/tui/installer/

# Startup files
$(INSTALL) -d \
$(DESTDIR)/etc/init.d \
$(DESTDIR)/etc/modprobe.d \
$(DESTDIR)/etc/modules-load.d \
$(DESTDIR)/etc/depmod.d \
$(DESTDIR)/etc/dracut.conf.d \
$(DESTDIR)/etc/udev/rules.d \
$(DESTDIR)/etc/systemd/system/systemd-udevd.d

$(INSTALL) -m755 startup/interface-rename-sideway startup/early-blacklist $(DESTDIR)/etc/init.d/
$(INSTALL) -m644 startup/functions $(DESTDIR)/etc/init.d/installer-functions
$(INSTALL) -m644 startup/early-blacklist.conf startup/bnx2x.conf $(DESTDIR)/etc/modprobe.d/
$(INSTALL) -m644 startup/blacklist $(DESTDIR)/etc/modprobe.d/installer-blacklist.conf
$(INSTALL) -m644 startup/modprobe.mlx4 $(DESTDIR)/etc/modprobe.d/mlx4.conf
$(INSTALL) -m644 startup/iscsi-modules $(DESTDIR)/etc/modules-load.d/iscsi.conf
$(INSTALL) -m644 startup/depmod.conf $(DESTDIR)/etc/depmod.d/
$(INSTALL) -m755 startup/preinit startup/S05ramdisk startup/S06mount $(DESTDIR)/$(INSTALLER_DIR)/
$(INSTALL) -m644 startup/01-installer.rules $(DESTDIR)/etc/udev/rules.d/
$(INSTALL) -m644 startup/systemd-udevd_depmod.conf $(DESTDIR)/etc/systemd/system/systemd-udevd.d/installer.conf

# Generate a multipath configuration from sm's copy, removing
# the blacklist and blacklist_exception sections.
sed 's/\(^[[:space:]]*find_multipaths[[:space:]]*\)yes/\1no/' \
< $(SM_ROOTDIR)/etc/multipath.xenserver/multipath.conf \
> $(DESTDIR)/etc/multipath.conf.disabled

# bootloader files
$(INSTALL) -D -m644 bootloader/grub.cfg $(DESTDIR)$(EFI_DIR)/grub.cfg
$(INSTALL) -D -m644 bootloader/grub.cfg $(DESTDIR)$(EFI_DIR)/grub-usb.cfg

sed -i '/^set timeout=[0-9]\+$/asearch --file --set /install.img' \
$(DESTDIR)$(EFI_DIR)/grub-usb.cfg

$(INSTALL) -D -m644 bootloader/isolinux.cfg $(DESTDIR)/boot/isolinux/isolinux.cfg

printf "echo Skipping initrd creation in the installer\nexit 0\n" \
> $(DESTDIR)/etc/dracut.conf.d/installer.conf
12 changes: 12 additions & 0 deletions answerfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ def parseFreshInstall(self):
results['preserve-settings'] = False
results['backup-existing-installation'] = False

results.update(self.parseAssembleRaid())
results.update(self.parseDisks())
results.update(self.parseInterface())
results.update(self.parseRootPassword())
Expand Down Expand Up @@ -170,6 +171,7 @@ def parseRestore(self):

results['install-type'] = INSTALL_TYPE_RESTORE

results.update(self.parseAssembleRaid())
backups = product.findXenSourceBackups()
if len(backups) == 0:
raise AnswerfileException("Could not locate exsisting backup.")
Expand Down Expand Up @@ -227,6 +229,7 @@ def parseCommon(self):
def parseExistingInstallation(self):
results = {}

results.update(self.parseAssembleRaid())
inst = getElementsByTagName(self.top_node, ['existing-installation'],
mandatory=True)
disk = normalize_disk(getText(inst[0]))
Expand Down Expand Up @@ -293,6 +296,15 @@ def parseDriverSource(self):
results['extra-repos'].append((rtype, address))
return results

def parseAssembleRaid(self):
results = {}
nodes = getElementsByTagName(self.top_node, ['assemble-raid'])
if nodes:
results['assemble-raid'] = True # possibly useless
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why useless? :)

logger.log("Assembling any RAID volumes")
rv = util.runCmd2([ 'mdadm', '--assemble', "--scan" ])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mix of simple and double quotes?

Also do you have a warn with pycodestyle? It seems to me that spaces next to the brackets can trigger a message.

return results

def parseDisks(self):
results = {}

Expand Down
4 changes: 4 additions & 0 deletions constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,7 @@ def error_string(error, logname, with_hd):
'var/lib/misc/ran-network-init',
'var/lib/misc/ran-storage-init',
]

# optional features
FEATURES_DIR = "/etc/xensource/features"
HAS_RAID_ASSEMBLE = os.path.exists(os.path.join(FEATURES_DIR, "raid-assemble"))
12 changes: 9 additions & 3 deletions diskutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,9 @@ def getMdDeviceName(disk):
rv, out = util.runCmd2(['mdadm', '--detail', '--export', disk],
with_stdout=True)
for line in out.split("\n"):
line = line.strip().split('=', 1)
if line[0] == 'MD_DEVNAME':
return line[1]
key, value = line.strip().split('=', 1)
if key == 'MD_DEVNAME':
return "md%s" % value

return disk

Expand All @@ -392,6 +392,12 @@ def getHumanDiskName(disk):
return disk[5:]
return disk

def getHumanDiskLabel(disk, short=False):
(vendor, model, size) = getExtendedDiskInfo(disk)
template = "{device} - {size} [{vendor} {model}]" if not short else "{device} - {size}"
return template.format(device=getHumanDiskName(disk), size=getHumanDiskSize(size),
vendor=vendor, model=model)

# given a list of disks, work out which ones are part of volume
# groups that will cause a problem if we install XE to those disks:
def findProblematicVGs(disks):
Expand Down
9 changes: 9 additions & 0 deletions doc/answerfile.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ Common Elements
Discovery on.


<assemble-raid/>?

Run `mdadm --assemble --scan` before looking for the device
specified in <primary-disk>, <existing-installation>, or
<backup-disk>.

Used to be the default behavior.


(Re)Install Elements
--------------------

Expand Down
18 changes: 18 additions & 0 deletions doc/features.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Features flags
==============

Some host-installer features are not enabled by default, and
downstream installers can activate them by creating a file in
/etc/xensource/features/ in their installer filesystem.

Currently available feature flags are:

raid-assemble

Detect Linux software-RAID (a.k.a "md") superblocks in disks, adds
a choice for the user to activate software RAID volumes, and do
not offer the user the ability to upgrade or restore a system on a
software-RAID device.

This only impacts the UI, the <assemble-raid/> answerfile
construct does not need this feature flag.
2 changes: 2 additions & 0 deletions startup/01-installer.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# disable 65-md-incremental.rules by telling an installer is running
ENV{ANACONDA}="1"
4 changes: 2 additions & 2 deletions tui/installer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ def out_of_order_pool_upgrade_fn(answers):
Step(uis.hardware_warnings,
args=[ram_warning, vt_warning],
predicates=[lambda _:(ram_warning or vt_warning)]),
Step(uis.scan_existing),
Step(uis.overwrite_warning,
predicates=[only_unupgradeable_products]),
Step(uis.get_installation_type,
predicates=[lambda _:len(results['upgradeable-products']) > 0 or len(results['backups']) > 0]),
Step(uis.get_installation_type),
Step(uis.upgrade_settings_warning,
predicates=[upgrade_but_no_settings_predicate]),
Step(uis.ha_master_upgrade,
Expand Down
94 changes: 72 additions & 22 deletions tui/installer/screens.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,6 @@ def fn10():
lvm.deactivateAll()
del lvm

tui.progress.showMessageDialog("Please wait", "Checking for existing products...")
answers['installed-products'] = product.find_installed_products()
answers['upgradeable-products'] = upgrade.filter_for_upgradeable_products(answers['installed-products'])
answers['backups'] = product.findXenSourceBackups()
tui.progress.clearModelessDialog()

diskutil.log_available_disks()

# CA-41142, ensure we have at least one network interface and one disk before proceeding
label = None
if len(diskutil.getDiskList()) == 0:
Expand Down Expand Up @@ -160,6 +152,22 @@ def hardware_warnings(answers, ram_warning, vt_warning):
if button == 'back': return LEFT_BACKWARDS
return RIGHT_FORWARDS

def scan_existing(answers):
tui.progress.showMessageDialog("Please wait", "Checking for existing products...")

if 'assemble-raid' in answers:
logger.log("Assembling any RAID volumes")
rv = util.runCmd2([ 'mdadm', '--assemble', "--scan" ])

answers['installed-products'] = product.find_installed_products()
answers['upgradeable-products'] = upgrade.filter_for_upgradeable_products(answers['installed-products'])
answers['backups'] = product.findXenSourceBackups()
tui.progress.clearModelessDialog()

diskutil.log_available_disks()

return RIGHT_FORWARDS

def overwrite_warning(answers):
warning_string = "Continuing will result in a clean installation, all existing configuration will be lost."
warning_string += "\n\nAlternatively, please contact a Technical Support Representative for the recommended upgrade path."
Expand Down Expand Up @@ -216,11 +224,44 @@ def get_admin_interface_configuration(answers):
return rc

def get_installation_type(answers):
entries = []

# If we were not already told to enable RAID, build a full list of
# RAID members, for filtering out from upgradable-products and
# backups, and to decide whether to propose to activate existing RAID.
raid_members = []
if constants.HAS_RAID_ASSEMBLE and "assemble-raid" not in answers:
for disk in diskutil.getQualifiedDiskList():
rv, out = util.runCmd2([ 'mdadm', '--examine', disk ], with_stdout=True)
if rv == 0 and re.search("Array UUID :", out):
raid_members.append(disk)

upgradeable_products = []
for x in answers['upgradeable-products']:
entries.append(("Upgrade %s" % str(x), (x, x.settingsAvailable())))
if x.primary_disk in raid_members:
logger.log("%s: disk %s in %s, skipping" % (x, x.primary_disk, raid_members))
continue
upgradeable_products.append(x)
backups = []
for b in answers['backups']:
entries.append(("Restore %s from backup" % str(b), (b, None)))
if not os.path.exists(b.root_disk):
logger.log("%s: disk %s not found, skipping" % (b, b.root_disk))
continue
if b.root_disk in raid_members:
logger.log("%s: disk %s in %s, skipping" % (b, b.root_disk, raid_members))
continue
backups.append(b)

entries = []
for x in upgradeable_products:
entries.append(("Upgrade %s on %s" % (x, diskutil.getHumanDiskLabel(x.primary_disk, short=True)),
(x, x.settingsAvailable())))
for b in backups:
entries.append(("Restore %s from backup to %s" % (b, diskutil.getHumanDiskLabel(b.root_disk, short=True)),
(b, None)))

if raid_members:
logger.log("Found a MD RAID on: %s" % ", ".join(raid_members))
entries.append(("Assemble software RAID volumes", ("RAID", None)))

entries.append( ("Perform clean installation", None) )

Expand All @@ -232,12 +273,21 @@ def get_installation_type(answers):
else:
default = None

if len(answers['upgradeable-products']) > 0:
text = "One or more existing product installations that can be upgraded have been detected."
if len(answers['backups']) > 0:
text += " In addition one or more backups have been detected."
if upgradeable_products or backups:
if upgradeable_products:
text = "One or more existing product installations that can be upgraded have been detected."
if backups:
text += " In addition one or more backups have been detected."
else:
text = "One or more backups have been detected."
if raid_members:
text += " Also, some disks have been identified as members of a sofware-RAID volume."
elif raid_members:
text = "Some disks have been identified as members of a sofware-RAID volume."
else:
text = "One or more backups have been detected."
text = "No existing product installation or backup was detected."
if raid_members:
text += " RAID volumes may themselves contain more upgradeable products or backups."
text += "\n\nWhat would you like to do?"

tui.update_help_line([None, "<F5> more info"])
Expand Down Expand Up @@ -306,6 +356,10 @@ def more_info(context):
elif isinstance(entry[0], product.XenServerBackup):
answers['install-type'] = constants.INSTALL_TYPE_RESTORE
answers['backup-to-restore'], _ = entry
elif entry[0] == "RAID":
# go rescan for products after assembling RAID volumes
answers['assemble-raid'] = True
return LEFT_BACKWARDS

return RIGHT_FORWARDS

Expand Down Expand Up @@ -556,9 +610,7 @@ def select_primary_disk(answers):
(boot, root, state, storage, logs) = diskutil.probeDisk(de)
if storage[0]:
target_is_sr[de] = True
(vendor, model, size) = diskutil.getExtendedDiskInfo(de)
stringEntry = "%s - %s [%s %s]" % (diskutil.getHumanDiskName(de), diskutil.getHumanDiskSize(size), vendor, model)
e = (stringEntry, de)
e = (diskutil.getHumanDiskLabel(de), de)
entries.append(e)

# we should have at least one disk
Expand Down Expand Up @@ -665,9 +717,7 @@ def select_guest_disks(answers):
# Make a list of entries: (text, item)
entries = []
for de in diskEntries:
(vendor, model, size) = diskutil.getExtendedDiskInfo(de)
entry = "%s - %s [%s %s]" % (diskutil.getHumanDiskName(de), diskutil.getHumanDiskSize(size), vendor, model)
entries.append((entry, de))
entries.append((diskutil.getHumanDiskLabel(de), de))

text = TextboxReflowed(54, "Which disks would you like to use for %s storage? \n\nOne storage repository will be created that spans the selected disks. You can choose not to prepare any storage if you wish to create an advanced configuration after installation." % BRAND_GUEST)
buttons = ButtonBar(tui.screen, [('Ok', 'ok'), ('Back', 'back')])
Expand Down