diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..d830bb97
--- /dev/null
+++ b/Makefile
@@ -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
diff --git a/answerfile.py b/answerfile.py
index 490b49ec..2dd5360e 100644
--- a/answerfile.py
+++ b/answerfile.py
@@ -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())
@@ -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.")
@@ -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]))
@@ -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
+ logger.log("Assembling any RAID volumes")
+ rv = util.runCmd2([ 'mdadm', '--assemble', "--scan" ])
+ return results
+
def parseDisks(self):
results = {}
diff --git a/constants.py b/constants.py
index 7b414802..1dc3d4eb 100644
--- a/constants.py
+++ b/constants.py
@@ -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"))
diff --git a/diskutil.py b/diskutil.py
index 952977b4..cac8d2a0 100644
--- a/diskutil.py
+++ b/diskutil.py
@@ -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
@@ -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):
diff --git a/doc/answerfile.txt b/doc/answerfile.txt
index 76221624..a4093b62 100644
--- a/doc/answerfile.txt
+++ b/doc/answerfile.txt
@@ -129,6 +129,15 @@ Common Elements
Discovery on.
+ ?
+
+ Run `mdadm --assemble --scan` before looking for the device
+ specified in , , or
+ .
+
+ Used to be the default behavior.
+
+
(Re)Install Elements
--------------------
diff --git a/doc/features.txt b/doc/features.txt
new file mode 100644
index 00000000..20fabd43
--- /dev/null
+++ b/doc/features.txt
@@ -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 answerfile
+ construct does not need this feature flag.
diff --git a/startup/01-installer.rules b/startup/01-installer.rules
new file mode 100644
index 00000000..5267d3ef
--- /dev/null
+++ b/startup/01-installer.rules
@@ -0,0 +1,2 @@
+# disable 65-md-incremental.rules by telling an installer is running
+ENV{ANACONDA}="1"
diff --git a/tui/installer/__init__.py b/tui/installer/__init__.py
index 7a8f588f..9e3f46d9 100644
--- a/tui/installer/__init__.py
+++ b/tui/installer/__init__.py
@@ -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,
diff --git a/tui/installer/screens.py b/tui/installer/screens.py
index e7b52721..4051ba42 100644
--- a/tui/installer/screens.py
+++ b/tui/installer/screens.py
@@ -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:
@@ -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."
@@ -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) )
@@ -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, " more info"])
@@ -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
@@ -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
@@ -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')])