# sysinstall.py vi:ts=4:sw=4:expandtab:
#
# Copyright (c) 2006 Three Rings Design, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright owner nor the names of contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import os
import farb
class ConfigSection(object):
"""
Abstract class implementing re-usable functions for install.cfg(8)
configuration sections.
"""
def _serializeOptions(self, output):
"""
Serialize all install.cfg options for this section
and the to an output file.
Concrete subclasses MUST provide a sectionOptions list as a class
attribute. This list must contain all valid install.cfg options for
the section, in the order required by sysinstall(8).
Given the sectionOptions list, this implementation will introspect
'self' for attributes with names that match the sectionOptions.
Any available attributes will be used, and any missing attributes
will be ignored.
@param output: Open, writable file handle
"""
for option in self.sectionOptions:
if hasattr(self, option):
output.write('%s=%s\n' % (option, getattr(self, option)))
def _serializeCommands(self, output, commands=None):
"""
Write out all commands listed in the sectionCommands class
attribute.
@param output: Open, writable file handle
@param commands: Commands to output. Defaults to sectionCommands.
"""
if (not commands):
commands = self.sectionCommands
for command in commands:
output.write('%s\n' % (command))
class NetworkConfig(ConfigSection):
"""
install.cfg(8) network configuration section.
"""
# Section option names
sectionOptions = (
'hostname', # New Server's Host Name
'domainname', # New Server's Domain Name
'netDev', # Network Interface
'nfs', # NFS Installation Media
'tryDHCP' # DHCP an address
)
# Default option values
tryDHCP = 'YES'
# Section commands
sectionCommands = (
'mediaSetNFS',
)
def __init__(self, section, config):
"""
Initialize network configuration for a given
installation.
@param section: ZConfig Installation section
@param config: ZConfig Farbot Config
"""
# Install-specific Options
self.hostname = section.hostname
self.domainname = section.domain
self.netDev = section.networkdevice
# FarBot-wide Options
self.nfshost = config.Releases.nfshost
self.nfspath = os.path.join(config.Releases.installroot, section.release.lower())
self.nfs = self.nfshost + ':' + self.nfspath
def serialize(self, output):
self._serializeOptions(output)
self._serializeCommands(output)
class DistSetConfig(ConfigSection):
"""
install.cfg(8) distribution set configuration section.
"""
# Section option names
sectionOptions = (
'dists', # Install these distribution sets
)
# Distribution sets are currently hardcoded
dists = 'base kernels GENERIC SMP doc games manpages catpages proflibs dict info src sbase scontrib scrypto sgnu setc sgames sinclude skrb5 slib slibexec srelease sbin ssecure ssbin sshare ssys subin susbin stools srescue'
# Section commands
sectionCommands = (
'distSetCustom',
)
def __init__(self, section, config):
"""
Initialize distribution set configuration for a given
installation.
@param section: ZConfig Installation section
@param config: ZConfig Farbot Config
"""
# Do nothing, distribution sets are currently hardwired
pass
def serialize(self, output):
self._serializeOptions(output)
self._serializeCommands(output)
class DiskLabelConfig(ConfigSection):
"""
install.cfg(8) FreeBSD labels (partition) configuration section.
"""
# Section option names are generated
# Section commands
sectionCommands = (
'diskLabelEditor',
)
def __init__(self, section, diskDevice):
"""
Initialize a disk label configuration for a given
partition map and device.
@param section: ZConfig PartitionMap section
@param diskDevice: Device to label (eg ad0s1)
"""
# Section option names are generated
self.sectionOptions = []
self.diskDevice = diskDevice
# Grab our partition map
for part in section.Partition:
# Build device + slice + partition number, and append it to
# sectionOptions
slice = self.diskDevice + '-' + part.getSectionName()
self.sectionOptions.append(slice)
# Partition settings
if (part.softupdates):
setattr(self, slice, "%s %d %s 1" % (part.type, part.size, part.mount))
else:
setattr(self, slice, "%s %d %s" % (part.type, part.size, part.mount))
# Ensure that partitions are in order (1 ... 9)
self.sectionOptions.sort()
def serialize(self, output):
self._serializeOptions(output)
self._serializeCommands(output)
class DiskPartitionConfig(ConfigSection):
"""
install.cfg(8) BIOS partition configuration section.
"""
# Section option names
sectionOptions = (
'disk', # Disk to partition
'partition', # Partitioning method
'bootManager', # Boot manage to install
)
# We hardcode the use of the entire disk
partition = 'all'
# Hardcode the use of the boot manager, too
bootManager = 'standard'
# Section commands
sectionCommands = (
'diskPartitionEditor',
)
def __init__(self, section, config):
"""
Initialize a disk partition configuration for a given
disk section.
@param section: ZConfig Disk section
@param config: ZConfig Farbot Config
"""
self.disk = section.getSectionName()
# Grab our partition map
# If it doesn't exist, complain loudly
self.diskLabelConfig = None
for map in config.Partitions.PartitionMap:
if (section.partitionmap.lower() == map.getSectionName()):
# Set up the disk labels. Always s1!
self.diskLabelConfig = DiskLabelConfig(map, self.disk + 's1')
break
def serialize(self, output):
self._serializeOptions(output)
self._serializeCommands(output)
self.diskLabelConfig.serialize(output)
class SystemCommandConfig(ConfigSection):
"""
install.cfg(8) system command configuration section.
"""
# Section option names
sectionOptions = (
'command', # Command name and arguments
)
# Section commands
sectionCommands = (
'system',
)
def __init__(self, cmd):
"""
Initialize system command configuration for a given
installation.
@param section: ZConfig command key value
"""
# Build command + options
self.cmd = cmd
setattr(self, 'command', "%s" % (cmd))
def serialize(self, output):
self._serializeOptions(output)
self._serializeCommands(output)
class PackageConfig(SystemCommandConfig):
"""
install.cfg(8) package install configuration section.
Sysinstall's dependency handling is seriously broken,
relying on an INDEX that doesn't necessarily reflect reality.
We skip the sysinstall package installation code entirely and
use a SystemCommand to call pkg_add(8) ourselves post-install.
"""
installPackageScript = os.path.join('/dist', os.path.basename(farb.INSTALL_PACKAGE_SH))
def __init__(self, section):
"""
Initialize package install configuration for a given
installation.
@param section: ZConfig Package section
"""
# /dist/install_package.sh <package name>
self.package = section.package
cmd = "%s %s" % (self.installPackageScript, self.package)
super(PackageConfig, self).__init__(cmd)
class InstallationConfig(ConfigSection):
"""
InstallationConfig instances represent a
complete install.cfg file for sysinstall(8)
"""
# Section option names
sectionOptions = (
'debug',
'nonInteractive',
'noWarn'
)
# Defaults
debug = 'YES'
nonInteractive = 'YES'
noWarn = 'YES'
# Commands needed to start up the interactive partitioner
interactivePartitionCommands = (
'diskInteractive="YES"', # Partition and label disks interactively
'diskPartitionEditor', # Run disk partition (MBR) editor
'diskLabelEditor' # Run disk label editor
)
# Pre-package commands
prePackageCommands = (
'diskLabelCommit', # Write disk labels to disk
'installCommit' # Write install distribution to disk
)
# Section commands
sectionCommands = (
'shutdown',
)
def __init__(self, section, config):
"""
Initialize a new installation configuration.
@param section: ZConfig Installation section
@param config: ZConfig Farbot Config
"""
self.name = section.getSectionName()
# Network configuration
self.networkConfig = NetworkConfig(section, config)
# Distribution sets
self.distSetConfig = DistSetConfig(section, config)
# Disks (Partitions and Labels)
self.diskPartitionConfigs = []
for disk in section.Disk:
diskPartitionConfig = DiskPartitionConfig(disk, config)
self.diskPartitionConfigs.append(diskPartitionConfig)
# Packages
self.packageConfigs = []
for psetName in section.packageset:
foundPset = False
for pset in config.PackageSets.PackageSet:
if (psetName.lower() == pset.getSectionName()):
foundPset = True
break
for package in pset.Package:
pkgc = PackageConfig(package)
self.packageConfigs.append(pkgc)
# System Commands
self.systemCommandConfigs = []
if (section.PostInstall):
for cmd in section.PostInstall.command:
systemCommandConfig = SystemCommandConfig(cmd)
self.systemCommandConfigs.append(systemCommandConfig)
def serialize(self, output):
# Global configuration options
self._serializeOptions(output)
# Network configuration
self.networkConfig.serialize(output)
# Select distribution sets
self.distSetConfig.serialize(output)
# Disk formatting
for disk in self.diskPartitionConfigs:
disk.serialize(output)
# If we have no diskPartitionConfigs, partition interactively
if len(self.diskPartitionConfigs) == 0:
self._serializeCommands(output, commands=self.interactivePartitionCommands)
# Commit installation to disk
self._serializeCommands(output, commands=self.prePackageCommands)
# Packages
for pkgc in self.packageConfigs:
pkgc.serialize(output)
# System Commands
for scc in self.systemCommandConfigs:
scc.serialize(output)
# Global commands
self._serializeCommands(output)
syntax highlighted by Code2HTML, v. 0.9.1