#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
"""
GRUB2 boot loader backend for pybootmgmt
"""
# pylint: disable=C0302
import ConfigParser
import errno
import fcntl
import filecmp
import grp
import libbe
import libbe_py
import os
import pwd
import shutil
import stat
import syslog
import sys
import tempfile
import time
def timestamp():
"""Returns a timestamp string with all significant digits
"""
#############################################################################
# menu.conf interface classes
#############################################################################
# pylint: disable=R0903
"""MenuConfigEntry represents one menu entry (section) from a
MenuConfigParser. Each entry is linked with the MenuConfigParser that
created it so that when it is modified, it can inform the parent
that state has been modified.
"""
"""Initialize the MenuConfigEntry
"""
if not mcp:
raise BootmgmtArgumentError('Bad arguments to %s.__init__' %
raise BootmgmtArgumentError('Bad section argument to %s.__init__' %
# section is the section name tuple:
# (<class type>, <0-based index>)
# options is a dictionary of all options in the section
if options:
else:
"""Update the options dict in this instance with the newdict passed in.
Detects if there are changes, and if there are, marks the parent
MenuConfigParser as dirty.
"""
del_keylist = []
if del_keylist:
for key in del_keylist:
if (not dirty and
if dirty:
"""Returns a dict with the options passed in, stringified.
"""
return options
"""Return a copy to the options dict so no one can modify it.
Ideally, it would be an immutable dict.
"""
"""Returns a floating point timestamp of the last modified time, or 0
if no timestamp exists.
"""
if ts_prop:
try:
except ValueError:
return 0
"""Return a string that represents the section name
"""
else:
return None
"""Match the section using the string passed.
"""
try:
return True
except ValueError:
return False
"""Returns the string representation of this menu configuration entry
"""
str_rep = ''
return str_rep
# pylint: disable=W0212
"""Compare two MenuConfigEntry instances based on their section names.
"""
# Compare the section tuples directly (the integer comparison
# for the third tuple element happens automatically):
return -1
return 1
return 0
"""Compares two MenuConfigEntry instances. the entry with the
most-recent modified timestamp wins, otherwise, the entry with the
lexically-largest section tuple wins.
"""
try:
cmpval = -1
else:
cmpval = 1
return cmpval
except (ValueError, KeyError):
pass # Can't compare using the modified timestamps, so revert
# to testing the section names
# pylint: enable=W0212
# pylint: enable=R0903
# pylint: disable=R0902
"""The MenuConfigParser class provides an interface to the menu.conf
file. The menu.conf file contains a set of sections, each (except for two)
of which describes a single Solaris boot entry in the
automatically-generated boot-loader menu. The syntax of the menu.conf is
that accepted by the ConfigParser class. The sections and properties
include:
[meta]
order = <comma-separated list of section names>
[global]
<Boot Loader Properties>
Zero or More of:
[<classname>:<index>]
title=<Menu entry title> [Required]
default={True|False} # If this is the default boot entry
transient={True|False} # If this is a transient entry
Other properties, depending on the <classname>:
bootfs=<bootfs value> [SolarisDiskBootInstance] [required]
kargs=<kernel arguments> [SolarisBootInstance] [optional]
kernel=<path to kernel file> [SolarisBootInstance] [optional]
boot_archive=<path to boot archive> [SolarisBootInstance] [optional]
"""
'# DO NOT EDIT THIS FILE DIRECTLY. CHANGES MAY NOT '
'BE PRESERVED.\n'
'# Changes to this file can be made indirectly via '
'bootadm(1M) subcommands.\n#\n')
"""Creates or loads the menu configuration from the specified file.
If the file does not exist, an empty menu configuration is created,
but is not written to disk (see the write() method). If new_conf
is True, the file's contents are not loaded; instead, an empty menu
configuration is created (but not written yet).
The following Exceptions may be generated:
BootmgmtConfigReadError -- If there's a problem reading config file.
"""
self.last_written_path = None
self.entry_list = []
self._meta_options = {}
self.global_options = {}
if not new_conf:
"""Attempt to load the file specified. If it does not exist,
proceed with an empty SafeConfigParser.
The following Exceptions may be generated:
BootmgmtConfigReadError -- If there's a problem reading config file.
"""
if not filename:
'configuration')
return
try:
return
raise BootmgmtConfigReadError('Error trying to read the '
'menu configuration file ', ioerr)
"""Creates MenuConfigEntry objects from the ConfigParser's section
list and adds them to the entry list. Also loads the meta and
global sections, if found.
"""
# Now create an MenuConfigEntry object for each section
# Parse the section name into a tuple:
new_entry = None
# Handle 'normal' menu entries
try:
except ValueError: # Not an int, ignore entry
sect)
# Globals are sanity-checked by MenuConfigParser consumers
if new_entry:
# pylint: disable=E0102,E0202,E1101
"""Returns the state of the internal dirty variable.
"""
"""Sets the state of the internal dirty variable. Emits a debug
message if the state of the internal dirty variable changed.
"""
# pylint: enable=E0102,E0202,E1101
"""Returns the filename of the menu.conf that this object was created
with.
"""
# pylint: disable=R0913
"""Add an entry to the menu configuration file.
The section_name parameter is used to derive the section
name.
If the propdict is not None, it contains
the properties used to initialize the new menu entry.
"""
return newent
# pylint: enable=R0913
"""Delete an entry by matching the object address to one in the list.
"""
"""Deletes one or more menu configuration entries. Entries are found
based by either matching the section_name argument (if cmp_fn is None)
or by invoking cmp_fn for each entry in the entry list. If cmp_fn
returns True, that entry is deleted. cmp_fn takes 1 argument--
the entry instance to consider for deletion).
If match_all is True, then all entries that match are deleted;
otherwise, only the first match is deleted.
Returns a count of the number of deleted entries.
"""
if match_all:
if cmp_fn:
elif section_name:
if section_name != x.section_name]
if count > 0:
else:
count = 0
count = 1
break
return count
"""CDS = comma-delimited string
"""
if stringlist:
return list_of_strings
return None
"""Set the propname property to a comma-separated string composed
of strings from stringlist.
"""
if stringlist:
"""Get a list of section headers that specify the order of menu
entries, if one exists.
"""
"""Set the list of section headers that specifies the order of menu
entries.
"""
"""Delete the order property from the meta section and mark this
instance dirty.
"""
"""Generates a new section title based on the existing sections and
the name passed in. Note that until the section is a part of a
MenuConfigEntry object and that object is added to this
MenuConfigParser's entry list, it's possible for this function to
generate multiple identical section names.
"""
# Now find the largest used index for the section_name
if not used_indices: # No other entries match, so index=0
else: # Search for and return a tuple with a free index
if not unused_indices:
raise BootmgmtError('Could not find a free section index!')
return section_tuple
"""Generate a new ConfigParser using the data from the MenuConfigEntry
"""
if self._meta_options:
if self.global_options:
try:
except ConfigParser.DuplicateSectionError:
else:
else:
return new_parser
"""Writes the menu configuration data to the menu configuration file.
If no changes have been made since the configuration was loaded,
write() will return without writing the file (unless force is True).
If alt_file is not None, the menu configuration will be written there.
Exceptions that may be generated:
BootmgmtConfigWriteError -- If the menu config file cannot be created
or if there's a problem writing to it.
"""
return
# First synch the MenuConfigParser state to a new ConfigParser:
# Use the alternate file, if specified; otherwise use the cached
# filename supplied to the constructor
# We cannot proceed without a filename
if not write_file:
raise BootmgmtArgumentError('menu configuration filename not '
'specified')
try:
raise BootmgmtConfigWriteError('Error writing menu '
'configuration file ', ioerr)
# pylint: enable=R0902
"""Implementation of the BootLoader interface for the GRUB2 boot
loader.
"""
SUPPORTED_PROPS = [
]
# These prefixes MUST NOT start with '/', or os.path.join() will not
# behave as we want below.
'uefi64': 'x86_64-efi'}
}
# List of modules that should be baked into the GRUB 2 image (i.e.
# available in rescue mode). Each key in PRELOADED_MODULES *must* be
# a list of strings (module names).
'uefi64': PRELOADED_MODULES_COMMON}
# These may look like duplicates of the BOOT_SUBDIRS, but they're separate
# because they're actualy arguments to the grub-mkimage command.
'uefi64': 'x86_64-efi'}
'uefi64': 'efi_gop'}
GFX_MODELIST = "1024x768x32;1024x768x16;800x600x16;640x480x16;" \
"640x480x15;640x480x32"
# This exit code is returned by grub-install and grub-mkimage if it is
# incompatible with the GRUB2 modules on which it was asked to operate.
# In the case of ODDBootConfig, this is a fatal error. For DiskBootConfig,
# we log this fact and then proceed with deferred boot loader activation.
# GRUB_OTHERS stores lists of 3-tuples (source, destination, perms)
# (source is relative to the data root (i.e. the root directory of a boot
# instance) and destination is relative to the config data root (i.e. the
# zfs top level dataset for a root pool). The destination is relative to
# (user, group, mode). If perms is None, GRUB_OTHERS_PERM_DEFAULTS is used.
'png': 'png',
'tga': 'tga'}
'share/grub/unicode.pf2'),
'unicode.pf2', None),
('boot/grub/splash.jpg',
'splash.jpg', None)]
'uefi64' : 'boot/grub/grub2netx64.efi'}
# Paths are relative to the zfs top level dataset:
# Due to a limitation in Solaris's mkfs -F pcfs, we cannot make a
# fat filesystem with nofdisk of size < 4M.
"""Probe for GRUB2 on a system.
"""
return None
get_current_arch_string() != 'x86'):
'platform')
return None
bootconfig.boot_fstype is not None):
try:
except BootmgmtError as bmerr:
return None
else:
raise BootmgmtUnsupportedOperationError('Boot class %s not '
"""This GRUB2 probe function searches the ODD's root, looking for
the GRUB2 core modules"""
'cdboot.img'),
'kernel.img'),
'kernel.img')]
try:
except BootmgmtNotSupportedError:
return None
return GRUB2BootLoader(**kwargs)
"""This GRUB2 probe function searches the net install image root,
looking for the GRUB2 NBP programs"""
if not artifacts:
return None
return GRUB2BootLoader(**kwargs)
"""This GRUB2 probe function invokes probe_artifacts to look for
the menu.conf file at the ZFS top-level dataset (if this is a ZFS-based
DiskBootConfig), as well as the GRUB2 kernel and utilities.
"""
if not artifacts:
return None
return GRUB2BootLoader(**kwargs)
"""Generic probe function for GRUB2.
"""
# Both the data root location must be specified
if dataroot is None:
raise BootmgmtNotSupportedError('dataroot is None')
try:
raise BootmgmtNotSupportedError('IOError when checking for '
'datafiles', ioerr)
"""Looks for the menu.conf and the NBPs. There are no "tools"
needed for NetBootConfig. The images are preassembled.
"""
artifacts = []
# This is the OS image's root; we need to look for the config
# files in the net_data_root.
try:
except OSError:
pass
try:
except BootmgmtNotSupportedError:
pass
return artifacts
"""Probe for artifacts found when used with a DiskBootConfig
"""
# probe_artifacts can either be called at BootLoader factory.get
# time or at runtime. If it's the latter, we need to get the
# most-recent data root in which to look for the artifacts.
if bootloader:
# pylint: disable=W0212
# pylint: enable=W0212
else:
else:
return []
return []
artifacts = []
# First look for the menu.conf file:
try:
# List of essential data files need to pass the disk probe:
'diskboot.img'),
'kernel.img'),
'kernel.img')]
try:
except BootmgmtNotSupportedError as excpt:
# In addition to the loader files themselves, we need to ensure
# that we have access to the grub-install program (and its dependent
# executables) in the currently-running system (otherwise, we'll
# have no way to install GRUB2).
if fwname == 'bios':
'sbin/grub-setup'),
'sbin/grub-probe'),
'sbin/grub-install'),
'bin/grub-mkimage')]
elif fwname == 'uefi64':
'sbin/grub-probe'),
'sbin/grub-install'),
'bin/grub-mkimage')]
else:
return []
for execname in disk_execs_probe_list:
try:
break
if not missing_tools:
return artifacts
"""Returns a list of artifacts for this BootLoader, given a
bootconfig to use for directory information.
"""
# Only supported for disk-based BootConfigs.
return []
return self._config_dir_disk()
# The config files are written to the root of the data dir.
else:
"""Constructor for GRUB2 BootLoader
"""
self.theme_os_path = None
self.theme_grub_path = None
self.theme_mods_set = None
self.theme_fonts_set = None
else:
"""Uninitialized all theme attributes (resets to default theme)
"""
self.theme_os_path = None
self.theme_grub_path = None
self.theme_mods_set = None
self.theme_fonts_set = None
"""Reset configuration information by starting with a new menu.conf.
"""
else:
else:
"""Loads the configuration, based on the type of BootConfig.
"""
inst_list = []
else:
raise BootmgmtUnsupportedOperationError('Stored configurations '
if inst_list:
return inst_list
return []
"""Returns the directory where configuration files are stored.
"""
else:
raise BootmgmtUnsupportedOperationError('Unknown filesystem: %s'
"""Load the menu configuration file and read bootloader properties
and boot instances from it. Autogenerate boot instances from the BEs
on the system. Returns True if boot instances were autogenerated (and
therefore the MenuConfigParser was updated).
"""
# merge in properties from the config file:
for key in menu_conf_bl_props:
try:
except BootmgmtUnsupportedPropertyError:
if do_add:
if boot_instances:
# Add the boot instances to the BootConfig instance:
"""Part of upgrade support: Correct splash image extension if
it's .xpm.gz.
"""
# BootLoader install() hooks
# (or temporary equivalent for non-Disk BootConfigs) file (from the global
# properties) and the rest of the menu.conf is used by the GRUB2
# autogeneration program in etc/grub.d/XX_solaris.
"""Generates the grub.cfg file(s) for GRUB2. Different approaches are
taken depending on the type of BootConfig this BootLoader is associated
with. For DiskBootConfig, we update the
$PREFIX/etc/default/grub2_defs.$FW file and the menu.conf before
exec()ing grub-mkconfig to do the actual file generation. For
ODDBootConfig and NetBootConfig, we create the grub.cfg by hand (since
the autogeneration scripts put system-specific GRUB2 commands into the
generated configuration file.)
"""
if self._boot_config is None:
msg = ('Cannot _write_config(%s) - _boot_config is None' %
raise BootmgmtInterfaceCodingError(msg)
# Determine the type of boot configuration we're dealing with, then
# call the tailored _write_config_<bootconfig_type> method.
else:
raise BootmgmtUnsupportedOperationError('Unsupported BootConfig '
"""Returns a list of firmware targets that this BootLoader must
handle at install()-time.
"""
return targets
else:
"""To generate the grub.cfg file, we invoke the grub-mkconfig
command. Before doing that, we need to create the grub2_defs.$FW
file, which consists of a number of environment variables whose
values are derived from the BootLoader properties. Once that file is
created, we must store the menu organizer's state (the menu.conf)
then we can invoke grub-mkconfig (since the Solaris autogenerator
plugin consumes the menu.conf file).
"""
# assemble_other_grub_files must be done before the grub.cfg is
# generated, as grub-mkconfig looks for specific files that
# assemble_other_grub_files copies.
if basepath:
return menu_conf_tuple_list
else:
return None
"""Write the menu.conf file to the system location if basepath is None.
Creates directories as needed.
May raise BootmgmtConfigWriteError if there was a problem creating
directories.
"""
if basepath:
# We need to return 2 tuples -- one for the menu.conf and one
# for the grub.cfg.
# If final_path is None, do not generate a tuple for menu.conf
final_path = None
if token:
else:
final_path = '/'
final_path += dest
# The menu.conf is stored in the root of the basepath
else:
raise BootmgmtConfigWriteError('Cannot find a place to write '
'%s for BootConfig class %s' %
tuple_list = []
None,
'root',
'root',
else:
tuple_list = None
return tuple_list
# pylint: disable=R0913
"""Generates a stub file that sources the parent dir's file of the
same name. This is suitable ONLY for DiskBootConfig and ODDBootConfig.
Returns a tuple if basepath is not None.
"""
if basepath:
else:
try:
raise BootmgmtConfigWriteError('Error during grub.cfg generation: '
except BootmgmtConfigWriteError:
raise
if contents:
if not contents_only:
if basepath:
if token:
else:
final_path = '/'
return (BootConfig.OUTPUT_TYPE_FILE,
None,
'root',
'root',
# pylint: enable=R0913
"""Generate the GRUB2 script code for the video driver load
function.
"""
if vidbackend:
video_insmod = ('function load_video_%s {\n'
'\tinsmod %s\n'
else:
video_insmod = 'function load_video_%s {\n\ttrue\n}'
return video_insmod
"""Generates the stub config files for the platform-specific config
directory.
"""
else:
video_insmod = ''
if def_dict:
multiboot = 'multiboot2'
module = 'module2'
else:
multiboot = 'multiboot'
module = 'module'
grubscript = ('set target=%s\n'
'set multiboot=%s\n'
'set module=%s\n'
'if [ -s %s ]; then\n'
'\tsource %s\n'
if basepath:
return [stub_grub_cfg, stub_custom_cfg]
"""Create and populate the grub.cfg file"""
# Before we can invoke grub-mkconfig, we need to transform
# the boot loader properties into a grub defaults file,
# then set the BOOTMGMT_GRUB2_DEFAULTS environment
# variable to its path. (The grub-mkconfig program will source
# file if the environment variable is set.)
grub_defs_path = None
try:
if defs_dict:
# The resulting grub.cfg from _internal_mkconfig is designed
# to work on all targets
else:
except BootmgmtConfigWriteError:
try:
except OSError:
pass
raise
finally:
if reset_env:
if (basepath and grub_defs_path and
try:
except OSError:
pass
# Not a fatal error
# pylint: disable=R0912
filesuffix=''):
"""Writes the grub.cfg file to the appropriate location. If
defs_dict is not None, internal grub-mkconfig is used.
"""
dest_path = ''
if basepath:
else:
else:
try:
raise BootmgmtConfigWriteError('Error during grub.cfg generation: '
except BootmgmtConfigWriteError:
raise
if basepath:
if token:
else:
final_path = '/'
return [(BootConfig.OUTPUT_TYPE_FILE,
None,
'root',
'root',
else:
# pylint: enable=R0912
"""Returns the token to use when constructing tuples returned from
install().
"""
token = None
if fstype == 'zfs':
elif fstype == 'ufs':
else:
return token
"""Moves the temporary grub.cfg.new to grub.cfg.
"""
try:
try:
raise BootmgmtConfigWriteError("Couldn't move %s to %s" %
# Move was successful, so now set the owner and mode properly:
try:
raise BootmgmtConfigWriteError("Couldn't set mode/perms on %s"
# pylint: disable=R0912
"""Invokes grub-mkconfig and directs its output to the outfile.
"""
try:
'Generating %s boot loader configuration files' % target,
finally:
# pylint: enable=R0912
# pylint: disable=R0914
"""Generate the GRUB2 defaults file either in the specified
path or in the system location. Returns the path to the defaults
file. This is suitable only for DiskBootConfig.
May raise BootmgmtConfigWriteError if there was a problem creating the
defaults file (or its parent directory).
"""
if basepath:
else:
try:
raise BootmgmtConfigWriteError('Error writing GRUB2 defaults',
return defs_path
# pylint: enable=R0914
"""Generate a dict with gfxterm defaults.
"""
defdict = {}
# If splash is None, the property does not exist, so use a
# default
if splash is None:
# We have to get the basename of the splashimage because
# we know it will be copied into the config_dir.
# If fore is None, the property does not exist, so use a default
if fore is None:
# If back is None, the property does not exist, so use a default
if back is None:
# The background color must have a 0 alpha so the
# splash image comes through.
# If a theme is to be used, it was previously copied into the
# proper place in _assemble_other_grub_files(). If theme_grub_path
# is non-None, use it to set the GRUB_THEME_DIRECT property; if
# theme_os_path is set, use it to set the GRUB_THEME property.
if self.theme_grub_path:
if self.theme_os_path:
return defdict
"""Generate a dictionary with grub defaults pertaining to the console.
"""
defdict = {}
# Regardless of the type of the GRUB2 console, always set the video
# backend; Solaris uses a framebuffer console by default and GRUB2
# may be called upon to set the graphics mode.
if video_backend:
# The gfxpayload modelist should be set regardless of the GRUB2
# console type also, since we may have Solaris instances that use
# framebuffer console that need this variable set in a specific
# part of the menu entry
if cons:
# 'console' is the default GRUB2 input terminal type. gfxterm
# is the default output terminal type if none are specified.
interm = None
outterm = 'console'
interm = None
outterm = 'gfxterm'
interm = 'serial'
outterm = 'serial'
else:
interm = None
outterm = None
if interm:
if outterm:
return defdict
"""Generate a dictionary of grub default keys and their values.
"""
defdict = {}
if timeout == '':
timeout = '0'
elif timeout is None:
# Set the default entry based on the list of default boot instances
if default_idxs:
return defdict
"""Convert a string of hex digits into a GRUB2 color specification.
"""
"""Convert the serial parameters property tuple into a GRUB2 serial
command. If params is None, the default serial command is used.
The form of the serial_params property is:
serial_params | A tuple containing (<portspec>,<speed>,<d>,
| <p>,<s>,<f>).
| <portspec> is currently defined to be a
| number (valid values depend on the platform,
| but '0' is ttya (com1) and '1' is ttyb (com2)).
| Serial console parameters (<d>=data bits,
| <p>=parity ('N','E','O'),<s>=stop bits ('0','1'),
| <f>=flow control ('H','S',None) for hardware,
| software, or none). The default is:
| ('0',None,None,None,None,None).
"""
cmd = 'serial'
if params is None:
return cmd + ' --unit 0'
return cmd
# If the port number is larger than 0xFF, assume it's an
# IO port number and use --port instead of --unit:
else:
return cmd
"""Copy all files associated with the theme whose theme
file is specified into the proper location. Returns a 3-tuple of
(1) tuples (if basepath is set) (or None if not), and (2)
the *GRUB path* to the theme_file (the GRUB path will be used
directly in the grub.cfg file) and (3) The OS path to the
theme_file.
"""
tuple_list = []
grub_path_prefix = '/@'
else:
grub_path_prefix = ''
if basepath:
theme_fonts_set = set()
theme_mods_set = set()
"dirnm is the source theme's directory"
continue
None,
'root', 'bin', 0644)
if modname:
return []
# We're doing a directory copy of an entire theme. If the
# destination exists, blow it away first, or the copytree will fail:
# Iterate through the source directory, using collect_tuples to
# collect the list of file tuples returned to the caller.
try:
raise
# The OS path to the theme file is the destination path
return None, None, None
# If the source directory does not exist, but the destination DOES,
# check to see if the theme file exists, and if it does, just use the
# existing theme in the destination. This covers the case where
# pybootmgmt is used in conjunction with a data source that does not
# include the specified theme.
# The OS path to the theme file is the destination path
# We're doing a directory copy of an entire theme. If the
# destination exists, blow it away first, or the copytree will fail:
try:
raise
return None, None, None
else:
"""Copy other needed GRUB2 files into the proper location.
Return the appropriate tuples (if basepath is set).
"""
# Assemble the theme files, if necessary
theme_tuples = None
# theme name alphanumeric validation was done in BootLoader
if themename:
else:
theme_file = None
else: # No theme property, so default to enable themeing with
# the default theme.
try:
if theme_file:
# If this is the default theme and we hit an error, quietly
# revert to non-themed graphical menus.
if not default_theme:
raise
if tuple_list:
if theme_tuples:
return tuple_list
if theme_tuples:
return theme_tuples
"""Using the list of 3-tuples passed in file_tuples, copy those files
to proper destination with the proper modes. This is suitable for
DiskBootConfig and ODDBootConfig only.
"""
tuple_list = []
if basepath:
else:
# If perms is None in the 3-tuple
if not perms:
try:
continue
# location
if not basepath:
try:
raise BootmgmtConfigWriteError("Couldn't set mode/perms on"
else:
if token:
else:
final_path = '/'
if tuple_list:
return tuple_list
"""A stripped-down barebones implementation of a GRUB2 menu generator.
First, a set of header script code is emitted (its contents depends on
the values in the defs_dict (a dict representation of the
correspond to the boot configuration's boot_instances list.
"""
if 'NO_CUSTOM_CFG' not in defs_dict:
'if [ -f /boot/grub/custom.cfg ]; then\n'
'\tsource /boot/grub/custom.cfg\n'
'fi\n')
"""Returns the list of lines in the script preamble stored in
the definitions dictionary passed in.
"""
if preamble:
return preamble
return []
# pylint: disable=R0912
"""Output the grub.cfg header for this target based on the defs_dict.
"""
lines = []
# If there is a preamble specified in the defs_dict, add that first:
# First, output the script for initializing the console device(s):
# load_video_$target is defined in the stub grub.cfg files (or the
# grub.cfg directly for NetBootConfig).
# NOTE: We always load video drivers because Solaris may need it
# for framebuffer console.
initted_devs = []
if outdev:
# Initting gfxterm must be done after all other console devices
# are initted because the terminal_output statement is embedded
# in the gfxterm init script block.
if 'gfxterm' in outdevs:
else:
initted_devs += [dev]
if do_gfxterm:
initted_devs += 'gfxterm'
else:
if indev:
if dev in initted_devs:
continue
initted_devs += dev
# Now add the timeout and quiet script:
if quiet:
else:
# finally, the default:
if default:
"""Return a list of lines that init the specified terminal device
"""
lines = []
# The caller knows that gfxterm must be the last console device
# processed (due to the use of terminal_output statements)
# We have several potential properties to handle here:
# GRUB_VIDEO_BACKEND, GRUB_BACKGROUND, GRUB_FORECOLOR, and
# GRUB_BACKCOLOR
if modes:
loaded_mods = []
# Add GRUB theme dependencies here:
if themefile:
# Emit module load lines for all required modules:
if self.theme_mods_set:
if self.theme_fonts_set:
# If loading the default font fails, it's not fatal
indent = '\t'
else:
indent = ''
# Add the font-load conditional statement:
# XXX - Revisit the hard-coded font path here?
if forecolor:
if backcolor:
if backimg:
# Loading the background image requires us to first load
# modules needed to access the filesystem on which it's stored
# Close the conditional
if need_fi: # If we need a fi (if theme code was emitted above)
elif dev == 'serial':
if serialcmd:
else:
return lines
# pylint: enable=R0912
"""To generate the grub.cfg file for an ODDBootConfig, we create the
menu by hand (we do NOT use the grub-mkconfig program).
"""
if not basepath:
raise BootmgmtInterfaceCodingError('basepath must be specified for '
'ODDBootConfig')
tuple_list = []
if target_list:
if of_tuples:
for target in target_list:
if not generated_grub_cfg:
# It doesn't matter which target's defs_dict we use here
# because _generate_grub_cfg is guaranteed to generate a
# config file without direct target-specific code (there may
# be references to things defined in the target-specific stubs
# though)
if gcfg_tuples:
# The stub files are the small script files that do target-specific
# things, then source the main config file
if stub_list:
if loader_tuples:
return tuple_list
"""To generate the grub.cfg file for a NetBootConfig, we create the
menu by hand (we do NOT use the grub-mkconfig program). Since we will
not use a graphical console while network booting, we do not need
to assemble other files (i.e. GRUB2 font files and splash screen image).
"""
if not basepath:
raise BootmgmtInterfaceCodingError('basepath must be specified for '
'NetBootConfig')
tuple_list = []
preamble = \
"""# begin preamble
regexp ".*/(.*)" $prefix -s 1:target
tr -s target - _ "$target"
if [ -z "$target" -o "$target" = "%(bios)s" ]; then
set multiboot=multiboot
set module=module
elif [ "$target" = "%(uefi64)s" ]; then
set multiboot=multiboot2
set module=module2
fi
target_dicts = {}
# Generate the target def dictionaries and the video driver load
# code so we can use them in the loop below.
for target in target_list:
preamble += "# end preamble"
for target in target_list:
if loader_tuples:
if not generated_grub_cfg:
# It doesn't matter which target's defs_dict we use here
# because _generate_grub_cfg is guaranteed to generate a
# config file without direct target-specific code.
# Elide the final stanza that searches for the custom.cfg --
# we have no use for it in a network boot scenario.
# Add the script preamble, which establishes the platform
# on which the script is executing and sets some key variables
# that the generated grub configuration file uses.
if gcfg_tuples:
# The grub_cfg tuples must be marked specially, so that
# the consumer knows that they can be placed in the
# tftp boot loader search path
config_tuples = []
for tupl in gcfg_tuples:
return tuple_list
# Override install so we can update the UEFI boot variables with the
# device list passed in. Failure to set the boot variables is NOT a
# fatal error; it will, however, be logged to syslog.
"""
"""
# No need to go further if we have tuples to return (no install
# was done to an actual device) or if location is empty
if tuple_list or not location:
return tuple_list
# If this is not a disk-based boot configuration, or if there were
# tuples returned from the main install() method (which implies we
# did not install onto actual physical devices) or if the system
# firmware is not UEFI, just return the tuple_list, if any.
return None
try:
except BootmgmtError as bmerr:
'device to %s. Manual intervention may be required '
return None
"""Write the GRUB2 boot loader to disk. We support 4 scenarios:
(1) BIOS systems, installation onto a DOS-partitioned disk with
a Solaris partition -- boot loader is embedded in the Solaris
boot slice (usually slice 8, but really it's any slice that
includes cylinder 0). The devname passed in can be directly
passed to grub-install, as grub-setup has been modified to write
the loader to the appropriate place in the Solaris partition;
(2) BIOS systems, installation onto a GPT-partitioned disk. A
BIOS Boot Partition (BBP) is required. The devname passed in
refers to the partition that includes the ZFS, not the BBP, so
we need to figure out if we're on a GPT-partitioned disk, and
pass the whole-disk node (p0) to grub-install so *IT* can
locate the BBP and install GRUB2 there;
(3) UEFI systems, installation onto a DOS-partitioned disk. The
device node refers to a VTOC slice, so we need to get the device
node for the EFI System Partition, mount it (if it's not already
mounted), and invoke grub-install, telling it to install the EFI
(4) UEFI systems, installation onto a GPT-partitioned disk.
Similar to (3).
"""
return
or
raise BootLoaderInstallError('Device node is not a slice: ' +
# The prefixdir MUST exist, otherwise grub-install will fail:
'--grub-directory=%s' % prefixdir,
'--pkglibdir=%s' % fulldatadir]
if preloads:
if force:
if verbose_file:
and target == 'bios'):
if target == 'bios':
if is_gpt:
else:
elif target == 'uefi64':
# We need to derive the EFI System Partition from the device
# passed in, mount it (if it's not already mounted), and pass
# the mountpoint as the instdev
if not esp_dev:
raise BootLoaderInstallError('Could not determine the EFI '
'System Partition (ESP) for device %s' % devname)
esp_mountpt = None
esp_tempdir = None
try:
if mntentdict:
# If there was a problem trying to determine if the ESP is
# already mounted, proceed anyway -- we'll fail during the
# mount if it was already mounted.
pass
if not esp_mountpt:
try:
raise BootLoaderInstallError('Error while trying to create '
'a temporary dir for mounting the ESP', ioerr)
try:
except BootmgmtError:
try:
except OSError:
pass
raise BootLoaderInstallError('Error while trying to mount '
'the EFI System Partition (%s)' % esp_dev)
else:
try:
except OSError:
default_statinfo = None
if default_statinfo is None:
else:
try:
except OSError:
pass
else:
' is not supported')
try:
if verbose_file:
print >> verbose_file, 'Output from boot loader ' \
print >> verbose_file, '<END OF OUTPUT>'
else:
'Boot loader installation')
except BootmgmtConfigWriteError as bmwerr:
# If we're not able to install the bootloader due to
# incompatibility between the GRUB2 utilities and the modules
# from the data directory, begin the deferred boot loader
# activation process by creating the required file in the
# root directory of the data_root.
try:
raise BootLoaderInstallError('Unable to install '
'GRUB2 boot loader and unable to create required '
'deferred boot loader activation file %s' % defer_file,
'GRUB2 could not be installed at this time. The GRUB2 '
'utilities on the root filesystem are older than (and '
'incompatible with) the GRUB2 modules in %s. '
'The new version of GRUB2 will be installed automatically '
'when booting the Solaris instance in which the new GRUB2 '
'modules are located.' % data_root)
else:
raise BootLoaderInstallError('GRUB2 installation failed',
finally:
if target == 'uefi64':
try:
try:
except OSError:
statinfo = None
try:
'default boot location -- ignoring')
if need_unmount:
# Final sanity check
if cmd_succeeded and (statinfo is None or
raise BootLoaderInstallError('Error while creating '
'the UEFI boot loader (size=0) on %s' % esp_dev)
finally:
if esp_tempdir:
try:
except OSError:
pass
"""Copies GRUB2 modules (and config files) to a directory under
basepath. Returns a list of tuples, each of which describes one of the
files copied. This is suitable for DiskBootConfig and ODDBootConfig
only.
"""
# No additional modules for netboot images -- everything is baked
# in.
return []
return []
tuple_list = []
# Iterate through all files in the modpath directory, excluding ones
# that are in the exclusion_list:
# We're only interested in *.img, *.mod, *.lst and efiemu*
continue
continue
try:
for tup in tuple_list:
try:
except OSError:
pass
raise BootmgmtConfigWriteError('Error while copying GRUB2'
else:
None,
'root', 'root', 0644))
return tuple_list
"""Construct the bootable grub2 image for the specified target. The
boot image is BootConfig-specific (BootLoaders associated with
ODDBootConfig instances get BIOS-targeted El Torito images and UEFI64-
targeted FAT filesystem images with embedded UEFI64-targeted boot
applications (suitable for direct inclusion in an ODD image);
NetBootConfig boot loaders get BIOS PXE images and UEFI64 net boot
application images).
"""
# microconfigs are needed even on BIOS targets, since the resulting
# image is used to build a USB image, where we will need to search.
else:
microconfig = None
tuples = None
if target == 'bios':
elif target == 'uefi64':
if target == 'bios':
elif target == 'uefi64':
if microconfig:
if tuples is not None:
return tuples
raise BootmgmtUnsupportedPlatformError('%s and target=%s not supported'
"""Create an El Torito image by invoking grub-mkimage with the proper
parameters, then prepending the result with cdboot.img.
"""
# Write the image to 'core.img', as that's where we'll look for it
# if we want to repurpose the ISO to make other bootable media
'part_gpt', 'ufs1']
# Finally, create the final file with cdboot.img prepended to the
# image we just created.
try:
raise BootmgmtConfigWriteError('Could not finish construction of '
None, None, None, None, None),
'root', 'root', 0644)]
if mod_tuples:
return tuple_list
microconfig=None):
"""Create a GRUB2 boot image for the specified target, named imagename,
with modlist modules embedded and preloaded. If microconfig is not
file.
"""
if microconfig:
'configfile'])
'-m', memdisk_path,
else:
memdisk_path = None
'%s boot image generation' % target)
if memdisk_path:
# Now we can remove the temporary memdisk image file:
try:
except OSError:
pass
"""Create a "tarfs" (tar file) with the microconfig specified. The
microconfig specified is the parent directory -- there must be a
"""
memdisk_img = None
orig_dir = None
try:
# Change into the directory that includes microconfig. This is
# required so that we can generate the tarfile that includes it.
# We change back to the original directory in the finally clause,
# below
return memdisk_img.name
if memdisk_img:
try:
except OSError:
pass
raise BootmgmtConfigWriteError('Error during GRUB2 memdisk '
'image construction', oserr)
finally:
if orig_dir:
try:
except OSError:
pass
# pylint: disable=R0912,R0913,R0914,R0915
"""Spawn a command.
"""
try:
if msg:
if return_stdout or not stdout_file:
else:
if return_stderr or stdout_file:
else:
stdout_output = ''
stderr_output = ''
# from both without blocking.
filedescs = []
try:
' pipe: %s', ioerr)
try:
' pipe: %s', ioerr)
if msg:
try:
try:
if retcode != 0:
'stderr was: %s\nstdout was: %s' % (retcode,
if not whatisthis:
whatisthis = 'command execution'
raise BootmgmtConfigWriteError('Error during %s: '
'%s returned error code %d. stderr was:\n%s' %
if msg:
if return_stdout or return_stderr:
return (stdout_output, stderr_output)
else:
return None
# pylint: enable=R0913
"""Create a FAT filesystem image that contains a GRUB2 boot application
(constructed by grub-mkimage) suitable for 64-bit UEFI systems. If a
microconfig file is specified, it is embedded into the image via the
-c argument to grub-mkimage.
"""
cleanup_list = []
def _uefi64_odd_cleanup():
"""Perform a cleanup of activities in LIFO order
"""
# pylint: disable=W0702
try:
func()
except:
pass
# pylint: enable=W0702
# Make a temporary directory that will be used to hold the image:
try:
raise BootmgmtConfigWriteError('Error during 64-bit UEFI El Torito '
'image construction', oserr)
# gzio must be in the list before normal because normal attempts
# to grub_dl_load(gzio) very early in GRUB startup (before $prefix
# is set) so the net result would be an error message from
# grub_dl_load() that $prefix is not set. We use gzio for the
# Solaris boot archive, so we might as well load it now anyway.
try:
except BootmgmtError as bmerr:
raise BootmgmtConfigWriteError('Error during 64-bit UEFI El Torito '
'image construction', bmerr)
# Create the image with a size that will fit the boot image and
# filesystem metadata
try:
raise BootmgmtConfigWriteError('Error during 64-bit UEFI El Torito '
'image construction', un_err)
# Associate the image with the lofi device
try:
except BootmgmtError as bmerr:
raise BootmgmtConfigWriteError('Error during 64-bit UEFI El Torito '
'image construction', bmerr)
# Create a pcfs filesystem on the lofi device
try:
except BootmgmtError as bmerr:
raise BootmgmtConfigWriteError('Error during 64-bit UEFI El Torito '
'image construction', bmerr)
# Create a temp dir to use as a mountpoint
try:
# Mount the pcfs filesystem just created
raise BootmgmtConfigWriteError('Error during 64-bit UEFI El Torito '
'image construction', un_err)
try:
except BootmgmtError as bmerr:
raise BootmgmtConfigWriteError('Error during 64-bit UEFI El Torito '
'image construction', bmerr)
# Copy the image onto the mounted pcfs filesystem
try:
raise BootmgmtConfigWriteError('Error during 64-bit UEFI El Torito '
'image construction', copyerr)
# Unmount the pcfs filesystem
try:
except BootmgmtError as bmerr:
try:
# Remove the lofi association
try:
except BootmgmtError as bmerr:
# Remove the boot image temporary file and temp dir
try:
None, None, None, None, None)]
if mod_tuples:
return tuple_list
# pylint: enable=R0912,R0914,R0915
"""Copies the NBP program for the specified target to the specified
path. Raises BootmgmtConfigWriteError if the copy failed.
"""
# pylint: disable=W0613
"""Create a GRUB2 image bootable via the PXE on systems with BIOS
firmware.
"""
# For now, do not create the image-- just use the one delivered
# into the installation image.
return [(BootConfig.OUTPUT_TYPE_BIOS_NBP,
None,
None,
None,
None,
None)]
"""Create a GRUB2 UEFI boot application image bootable via the native
UEFI network boot mechanism.
"""
# For now, do not create the image-- just use the one delivered
# into the installation image.
return [(BootConfig.OUTPUT_TYPE_UEFI64_NBP,
None,
None,
None,
None,
None)]
# pylint: enable=W0613
"""Create a small configuration file whose purpose is one:
(a) to locate the REAL root device (since the microconfig file will
be loaded from a memdisk baked inside the GRUB2 image), or (b) to
source the REAL configuration file (i.e. when booting from the network,
the microconfig contains the GRUB2 script that searches the tftp
hierarchy, in accordance with the Solaris-defined search order for
the GRUB2 configuration file.)
"""
lines = []
if root_search:
else:
# Future enhancement: Generate the GRUB2 NetBootConfig microconfig
# here by using the script delivered to the install image, making
# sure to set prefix to a directory whose last component is the
# platform name (that means that _grub2_mkimage_generic must be
# modified also to parameterize the --prefix argument given to
# grub-mkimage)
raise BootmgmtUnsupportedOperationError('Microconfig creation '
'not supported for class %s' %
if not lines:
return None
tmpdir = None
try:
# The microconfig file must be stored in a temp dir, under
# will be told to find it (prefix will be set to
# Return the parent dir holding the config file
return tmpdir
if tmpdir:
raise BootmgmtConfigWriteError('Error while trying to write '
'GRUB2 microconfig', err)
"""Create directories for dirname(basepath).
May raise BootmgmtConfigWriteError if there was a problem creating the
directories.
"""
"""Create directories for path.
May raise BootmgmtConfigWriteError if there was a problem creating the
directories.
"""
try:
raise BootmgmtConfigWriteError('Could not create '
'directory', oserr)
finally:
"""GRUB2MenuOrganizer includes shared code between the GRUB2 pybootmgmt
Methods are provided to get an ordered list of boot instances from the
system (via the autogen backend (which gets the list of BEs and
generates a boot instance for each BE) if none exist in the menu.conf) and
the rest from the menu.conf, which has sections for each customized boot
instance (and, therefore, menu entry)) and to synch a list of boot
instances with the menu.conf.
"""
"""Constructor for GRUB2MenuOrganizer
"""
"""Pseudoproperty returns True of the underlying MenuConfigParser is
dirty.
"""
return False
"""Accessor for the path to the last menu.conf written.
"""
else:
return None
"""Start over with a new (empty) menu configuration
"""
"""Returns a dictionary of properties from the global section of the
"""
return blprops
"""Load the menu.conf file and processes the sections, generating
BootInstance objects and returning the list to the caller.
"""
# The total number of boot instances added to the BootConfig instance
# stored in self._boot_config is equal to the number of BE sections
# in the menu.conf file (including the transient entry, if it exists).
# If there are no BE sections, the autogenerator is used to populate
# the list.
# Get the list of instances from the menu.conf file:
if inst_list:
# If an explicit ordering is specified in the menu configuration
# file, rearrange inst_list and return it.
if explicit_order:
if do_update:
# If no instances are present, generate instances for the BEs
# on the system
['solaris'])
# Now inst_list has the full set of boot instances
return inst_list
"""Given a list of boot instances and a boot loader properties dict,
synchronize the underlying MenuConfigParser in preparation for writing
the menu.conf.
"""
# First update the properties in the [global] section
try:
strlist = []
except TypeError:
continue
if (self._boot_config and
else:
valid_bootfses = None
# Loop through all boot instances, keeping track of the order
# for the order property, and synchronizing the menu entry with the
# boot instance.
order_list = []
# Filter out Solaris boot instances that refer to deleted BEs
# (this is only possible if the boot instance has a bootfs
# member)
continue
if custom_entry:
# Synch boot instance to custom entry:
else:
# Check if this class has a corresponding emitter in
# the GRUB2MenuEntryEmitter class. If it does not,
# do not add it to menu.conf.
None)
if emitter:
else:
custom_entry = None
if custom_entry:
# Remove sections in the menu.conf file that correspond to deleted boot
# instances (i.e. all those not in the order_list)
(lambda x: x.section_string not in order_list))
"""Loads the menu configuration file.
"""
# This will succeeds either if the menu.conf is loaded successfully,
# or if the menu.conf is not present. In the latter case, an empty
# menu.conf is created (but not written, yet).
if new_conf_path:
"""Save the menu configuration to stable storage.
"""
"""Convert boot instance to a dictionary suitable for serialization
in the menu.conf file. The bulk of the work is performed by methods
tailored for each type of BootInstance.
"""
return inst.serialization_dict()
else:
return {}
"""Convert a propdict from a menu.conf section into a boot instance.
The type of BootInstance is given by the string section_type.
"""
if classtype:
# Make a copy of the properties from the section and remove the
# 'modified' attribute, since it has nothing to do with creation
# of a boot instance:
if 'modified' in attributes:
del attributes['modified']
# The default property must be a bool, else we get an exception
# at BootInstance construction time.
str(attributes)))
# Add the platform value from the associated BootConfig so that
# the BootInstance is properly initialized.
if plat:
# BootInstance instantiation takes a path as the first parameter
# and **kwargs for all others.
# pylint: disable=W0142
return classtype(None, **attributes)
# pylint: enable=W0142
return None
"""Copy properties from the boot instance to the MenuConfigEntry
"""
propdict))
"""Rearranges the boot instance list passed in to match the order
specified. The boot_instances list is modified in place.
"""
# The explicit_order list contains strings that are of the forms:
# '<classtype>|<index>'. Note that if the explicit order list is
# incomplete, order is undefined for other entries. If a boot
# instance is referenced in the explicit order list, but it doesn't
# exist on the system, that order entry is ignored
ordered_list = []
for current in explicit_order:
if inst is None: # Skip instance slots marked with None
continue
# Each instance in the instance list is guaranteed to have
# a _menu_conf_entry, so there is no need to use getattr here.
# pylint: disable=W0212
# Found a match -- add the instance to the temp list
# and mark the slot in boot_insts so we don't duplicate
# effort
boot_insts[instidx] = None
break
# pylint: enable=W0212
if not found: # the order property is out of date!
# Now update boot_instances with the ordered_list first:
cur_inst = 0
for inst in ordered_list:
cur_inst += 1
# Then any leftover instances that were not specified explicitly.
# Note that the relative order of these unmatched instances is
# not disturbed.
for inst in boot_insts:
if inst:
cur_inst += 1
return need_update
"""Builds a list of boot instances, each of which corresponds to a
single entry-type section in the menu.conf file.
"""
custom_inst_list = []
# Ignore entries with sections whos names are tuples with <> 2
# items:
continue
# Store the menu.conf entry this boot instance was created from
# for later use (i.e. sorting, etc.)
return custom_inst_list
"""Escape a menuentry title. In addition to the regular escapes,
'>' must be escaped (replaced with '>>').
"""
if escstr:
return escstr
"""Escape a string so it evaluates to its raw value in grub.cfg.
"""
# Quotation marks must be escaped, as must backslashes
return cookedstr
"""Emits GRUB menuentry blocks for the grub.cfg file.
"""
"""If fileobj is None, STDOUT is used. vardict is the dictionary
that holds variables that may be used by the menu entry
generators. If it's None, os.environ is used. If force_grub2 is
True, we instantiate the BootConfigs with a loaderclass keyword arg
to ensure we get the BootInstances from GRUB2 and not a different
(perhaps higher-ranked) BootLoader, whose BootInstances may be in
the BootConfig's boot_instances list.
"""
if not fileobj:
else:
self._rpool_props = {}
if not vardict:
else:
"""Caches zpool properties used by the menu generator methods.
"""
try:
# The GRUB2 guid for a zfs pool is a straight hex conversion--
# there are NO dashes, but there ARE leading zeroes!
except ValueError as valerr:
"""Emit entries to the fileobj specified in __init__. This should only
be invoked when this class is instantiated by the GRUB2 autogen script.
"""
for rpool in all_root_pools:
if self.force_grub2:
else:
argdict = None
# Filter out entries that managed to exist despite
# referring to a nonexistant BE:
if (valid_bootfses and
continue
# Emit code to check for the presence and nonzero size of
# a GRUB Legacy configuration file. If it exists, extract
# the entries into a submenu.
'\t\t\textract_legacy_entries_source "$2"\n')
"""Call the BootInstance-specific function to emit a menuentry block
"""
None)
tabs = ''
# If the boot instance is target-specific, emit code that causes
# GRUB2 to only process the menu entry when running on that target.
tabs = '\t'
else:
if line == '':
else:
if needclose:
'on this firmware" {\n' % clean_title)
else:
"""Returns a string containing the search command to use for the
specified pool.
"""
# XXX Add hints:
else:
# XXX Add hints:
# pylint: disable=C0103
"""Emits a menuentry for a Solaris network boot instance.
"""
entry = []
# If the parent boot configuration isn't a NetBootConfig, we'll need an
# explicit load of the network driver.
else:
# Net boot instances frequently have $ strings embedded in the
# kernel arguments. If any are found, escape them from the grub2
# scripting parser
if kargs:
# pylint: disable=W0613
"""Emits a menuentry for a ODD-based Solaris instance.
"""
entry = []
# If the parent boot configuration isn't on an ODD, we'll need an
# explicit load of iso9660.
if search:
# pylint: disable=W0212
"""Custom emitter for a SolarisDiskBootInstance
"""
entry = []
# Cover all bases by loading part_msdos, part_sunpc, and part_gpt
rootpath = None
if search:
else:
# else, assume ZFS
if not kargs:
return
# If the BootConfig that this boot instance is a member of is a
# disk boot config (i.e. we invoked grub-mkconfig to generate the
# grub.cfg file) AND the console type of this BootConfig is NOT
# graphical, then we need to manually add a call to load_video
# (the body of which is emitted by the 00_header script). This is
# essential so that GRUB can find and set graphics modes for Solaris
# instances that use framebuffer console.
if bootconfig and boot_loader:
try:
except BootmgmtUnsupportedPropertyError:
pass
else:
# Common code for the remainder of the entry:
# pylint: enable=W0613
"""Returns a search command string that includes the identifying file
from either the bootinst (tried first) or boot_loader.
"""
# If the boot instance specifies a identifying file, use it to
# find $root
else:
ident_file = None
# Fall back on the boot loader's identifying file to find $root
if not ident_file and boot_loader:
if ident_file:
return 'search --no-floppy --file --set=root ' + ident_file
else:
return None
"""Generic code for finishing a Solaris boot instance's menuentry
"""
if gfxpayload_modes:
# Only 64-bit is supported, so replace the format-string here:
try:
except KeyError:
try:
except KeyError:
if is_encrypted:
multiboot = 'multiboot2'
module = 'module2'
else:
multiboot = '$multiboot'
module = '$module'
if rootpath:
if gfxpayload_modes:
else:
# If the kernel specified has another path prepended, ensure
# that the second parameter to multiboot[2] is the boot-archive-
# relative kernel path.
if idx > 0:
else:
ba_rel_kern = '$kern'
if gfxpayload_modes:
return entry
"""Checks if the specified dataset (or implied dataset) has encryption
enabled, and if so, emits the proper commands to prompt for and set
the zfs key. Returns True if the dataset is encrypted.
"""
if bootfs:
elif rpool:
else:
dataset_name = None
return True
return False
"""Handle the ZFS-specific part of menuentry generation. Returns the
kargs to use in the entry.
"""
if bootfs:
else: # No bootfs -- check for rpool
if not rpool:
return None
if search_cmd:
# If this is an encrypted dataset, prompt user for the password
if bootfs:
else:
rootpath = '/${zfs_rootpath}/@'
if kargs:
else:
kargs = '-B $zfs_bootfs'
# pylint: disable=W0613
"""Custom emitter for a ChainDiskBootInstance
"""
entry = []
return entry
try:
# GRUB2 uses 1-based partition indices
except ValueError:
# Special case -- use of letters: GRUB2 does not
# support using letters to access VTOC-labelled drives
# so convert the letter into a numerical index
tuple_item <= 'z'):
else:
try:
# GRUB2 uses 1-based partition indices
except ValueError:
diskstr += ')'
if inst.forceactive:
else:
return entry
# pylint: enable=C0103,W0212,W0613
def bootloader_classes():
"""Returns a list of boot loader classes, consumed by the boot loader
factory.
"""
return [GRUB2BootLoader]