Files
core-build/build/tools/batch_install.py
2019-03-11 15:53:41 -03:00

197 lines
7.6 KiB
Python

#!/usr/bin/env python
# Batch installation script for FreeNAS/TrueNAS.
# This is intended to be run as part of the build tests.
# It takes one option, and at least one argument.
# The one option is for an install.conf file location,
# which can be used to set some defaults
# The first argument is the location of the build directory
# (it will determine everything else from there, including
# setting the search path for python); the optional other
# arguments are disk names to be installed onto.
import os, sys
import argparse
def ParseConfig(path="/etc/install.conf"):
"""
Parse a configuration file used to automate installations.
The result is a dictionary with the values parsed into their
correct types. The supported settings are:
minDiskSize
maxDiskSize: A value indicationg the minimum and maximum disk size
when searching for disks. No default value.
whenDone: A string, eithe reboot, wait, or halt, indicating what action to
take after the installation is finished. Default is to reboot.
upgrade: A string, "yes" or "no", indicating whether or not to do an upgrade.
Default is not to upgrade.
mirror: A string, "yes" or "no" or "force", indicating whether or not to install
to a mirror.
format: A string, either "efi" or "bios", indicating how to format the disk.
Default is None, which means not to format at all.
disk,
disks A string indicating which disks to use for the installation.
diskCount: An integer, indicating how many disks to use when mirroring.
By default, it will select the first disk it finds. If mindDiskSize and/or maxDiskSize
are set, it will use those to filter out disks. If mirror is True, then it will use either
two or diskCount (if set) disks to createa mirror; if mirror is set to "force", then it
will fail if it cannot find enough disks to create a mirror.
"""
def yesno(s):
if s.lower() in ["yes", "true"]:
return True
return False
rv = {
"whenDone" : "reboot",
"upgrade" : False,
"mirror" : False,
}
try:
with open(path, "r") as f:
for line in f:
line = line.rstrip()
if line.startswith("#"):
continue
if not '=' in line:
continue
(key, val) = line.split("=")
if key in ["minDiskSize", "maxDiskSize"]:
val = ParseSize(val)
elif key == "whenDone":
if val not in ["reboot", "wait", "halt"]:
continue
elif key == "upgrade":
val = yesno(val)
elif key == "format":
if val not in ["efi", "bios"]:
continue
elif key == "mirror":
if val.lower() == "force":
val = True
rv["forceMirror"] = True
else:
val = yesno(val)
elif key in ["disk", "disks"]:
key = "disks"
val = var.split()
elif key == "diskCount":
val = int(val)
rv[key] = val
except:
pass
return rv
arg_parser = argparse.ArgumentParser(description="Batch Installer")
arg_parser.add_argument('-C', '--config',
dest='config_file',
default='/etc/install.conf',
help='Specify configuration file')
arg_parser.add_argument("buildroot", nargs=1,
help='Specify root of build (e.g., /build/freenas/freenas/_BE)')
arg_parser.add_argument("disks", nargs='*',
help='Optional list of disks to install onto')
args = arg_parser.parse_args()
print("Args = {}".format(args))
# We locate certain files and libraries in the build root.
for p in ["objs/instufs/usr/local/lib"]:
sys.path.append(os.path.join(args.buildroot[0], p))
avatar_file = os.path.join(args.buildroot[0], "objs/instufs/etc/avatar.conf")
from bsd.sysctl import sysctlbyname
from ixsystems.installer.Install import Install, InstallationError
import ixsystems.installer.Utils as Utils
from ixsystems.installer.Utils import InitLog, LogIt, Title, Project, SetProject
from ixsystems.installer.Utils import IsTruenas, LoadAvatar
from ixsystems.installer.Menu import validate_disk, ValidationError
import freenasOS.Manifest as Manifest
import freenasOS.Configuration as Configuration
InitLog(sys.stdout)
LoadAvatar(avatar_file)
install_config = ParseConfig(args.config_file)
# The big potential conflict between the config file and the
# command-line arguments are disks. If no disks were specified, then
# we have to depend on the paramters in the config file.
if args.disks:
install_config['disks'] = args.disks
if not install_config.get("disks", None):
min_disk_size = install_config.get("minDiskSize", 0)
max_disk_size = install_config.get("maxDiskSize", 0)
diskCount = install_config.get("diskCount", 1)
mirror = install_config.get("mirror", False)
force_mirror = install_config.get("forceMirror", False)
if force_mirror and diskCount < 2:
diskCount = 2
# Let's try searching for some disks then
system_disks = sysctlbyname("kern.disks")
if ord(system_disks[-1]) == 0:
system_disks = system_disks[:-1]
possible_disks = []
for disk_name in system_disks.split():
try:
validate_disk(disk_name)
disk = Utils.Disk(disk_name)
if min_disk_size and disk.size < min_disk_size:
LogIt("Disk {} is too small".format(disk_name))
comtinue
if max_disk_size and disk.size > max_disk_size:
LogIt("Disk {} is too large".format(disk_name))
possible_disks.append(disk_name)
except ValidationError as e:
LogIt(e.message)
if len(possible_disks) >= diskCount:
break
if not possible_disks:
LogIt("No suitable disks found for installation")
raise InstallationError("No Disks Found")
if len(possible_disks) < diskCount:
LogIt("Needed {} disks, only found {}".format(diskCount, len(possible_disks)))
raise InstallationError("Insufficient number of disks found")
install_config['disks'] = possible_disks
if install_config["upgrade"]:
LogIt("Currently cannot handle an upgrade, sorry")
raise InstallationError("Cannot currently handle an upgrade")
install_disks = []
for disk in install_config["disks"]:
install_disks.append(Utils.Disk(disk))
# Okay, at this point we are nearly all set. Let's set up some variables
manifest_path = os.path.join(args.buildroot[0],
"release/LATEST/{}-MANIFEST".format(Project()))
manifest = Manifest.Manifest()
manifest.LoadPath(manifest_path)
package_dir = os.path.join(args.buildroot[0], "release/LATEST/Packages")
fn_conf = Configuration.SystemConfiguration()
template_dir = os.path.join(args.buildroot[0], "objs/instufs/data")
try:
Install(interactive=False,
manifest=manifest,
config=fn_conf,
package_directory=package_dir,
disks=install_disks,
efi=install_config.get("format", "bios") == "efi",
upgrade=False,
data_dir=template_dir,
password=install_config.get("password", ""),
trampoline=True)
except BaseException as e:
LogIt("Got exception {} during install".format(str(e)))
raise e
sys.exit(0)