7038706 incorporate pybootmgmt
authorSanjay Nadkarni <nadkarni@sun.com>
Thu, 21 Apr 2011 23:16:03 -0600
changeset 1090 6ac92c9d29e0
parent 1089 40fceca9515a
child 1091 aaabd219700a
7038706 incorporate pybootmgmt
usr/src/Makefile.master
usr/src/lib/Makefile
usr/src/lib/Makefile.lib
usr/src/lib/Makefile.targ
usr/src/lib/bootmgmt/Makefile
usr/src/lib/bootmgmt/__init__.py
usr/src/lib/bootmgmt/backend/Makefile
usr/src/lib/bootmgmt/backend/__init__.py
usr/src/lib/bootmgmt/backend/autogen/Makefile
usr/src/lib/bootmgmt/backend/autogen/__init__.py
usr/src/lib/bootmgmt/backend/autogen/solaris.py
usr/src/lib/bootmgmt/backend/bootvars/Makefile
usr/src/lib/bootmgmt/backend/bootvars/__init__.py
usr/src/lib/bootmgmt/backend/bootvars/sparc/Makefile
usr/src/lib/bootmgmt/backend/bootvars/sparc/__init__.py
usr/src/lib/bootmgmt/backend/bootvars/sparc/solaris.py
usr/src/lib/bootmgmt/backend/bootvars/x86/Makefile
usr/src/lib/bootmgmt/backend/bootvars/x86/__init__.py
usr/src/lib/bootmgmt/backend/bootvars/x86/solaris.py
usr/src/lib/bootmgmt/backend/fw/Makefile
usr/src/lib/bootmgmt/backend/fw/__init__.py
usr/src/lib/bootmgmt/backend/fw/bios.py
usr/src/lib/bootmgmt/backend/fw/obp.py
usr/src/lib/bootmgmt/backend/fw/uefi64.py
usr/src/lib/bootmgmt/backend/loader/Makefile
usr/src/lib/bootmgmt/backend/loader/__init__.py
usr/src/lib/bootmgmt/backend/loader/grub2.py
usr/src/lib/bootmgmt/backend/loader/legacygrub.py
usr/src/lib/bootmgmt/backend/loader/menulst.py
usr/src/lib/bootmgmt/backend/loader/sbb.py
usr/src/lib/bootmgmt/bootarchive.py
usr/src/lib/bootmgmt/bootconfig.py
usr/src/lib/bootmgmt/bootinfo.py
usr/src/lib/bootmgmt/bootloader.py
usr/src/lib/bootmgmt/bootutil.py
usr/src/lib/bootmgmt/pysol.py
usr/src/pkg/Makefile
usr/src/pkg/manifests/system-library-install.mf
usr/src/pkg/transforms/disable_depend_checks
--- a/usr/src/Makefile.master	Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/Makefile.master	Thu Apr 21 23:16:03 2011 -0600
@@ -125,6 +125,14 @@
 ROOTPYTHONVENDORSOLINSTALLTARGETLIBNVPAIR = $(ROOTPYTHONVENDORSOLINSTALLTARGET)/libnvpair
 ROOTPYTHONVENDORSOLINSTALLTARGETSHADOW = $(ROOTPYTHONVENDORSOLINSTALLTARGET)/shadow
 ROOTPYTHONVENDORINSTALLTRANSFER =       $(ROOTPYTHONVENDORSOLINSTALL)/transfer
+ROOTPYTHONVENDORBOOTMGMT=	$(ROOTPYTHONVENDOR)/bootmgmt
+ROOTPYTHONVENDORBOOTMGMTBKND=	$(ROOTPYTHONVENDORBOOTMGMT)/backend
+ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN=	$(ROOTPYTHONVENDORBOOTMGMTBKND)/autogen
+ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS=	$(ROOTPYTHONVENDORBOOTMGMTBKND)/bootvars
+ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC=	$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/sparc
+ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86=	  $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/x86
+ROOTPYTHONVENDORBOOTMGMTBKNDFW=	$(ROOTPYTHONVENDORBOOTMGMTBKND)/fw
+ROOTPYTHONVENDORBOOTMGMTBKNDLOADER=	$(ROOTPYTHONVENDORBOOTMGMTBKND)/loader
 ROOTAUTOINST=		$(ROOT)/usr/share/auto_install
 ROOTAUTOINSTSCPROFILES=	$(ROOTAUTOINST)/sc_profiles
 ROOTSBIN=		$(ROOT)/sbin
--- a/usr/src/lib/Makefile	Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/lib/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -25,7 +25,8 @@
 
 include $(SRC)/Makefile.master
 
-SUBDIRS=	install_common \
+SUBDIRS=	bootmgmt \
+		install_common \
 		install_utils \
 		libaiscf \
 		libaiscf_pymod \
--- a/usr/src/lib/Makefile.lib	Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/lib/Makefile.lib	Thu Apr 21 23:16:03 2011 -0600
@@ -76,6 +76,10 @@
 ROOTPYTHONVENDORSOLINSTALLTARGETCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORSOLINSTALLTARGET)/%)
 ROOTPYTHONVENDORSOLINSTALLTARGETLIBS=	$(CPYTHONLIBS:%=$(ROOTPYTHONVENDORSOLINSTALLTARGET)/%)
 
+ROOTPYTHONVENDORBOOTMGMTTARGETMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTTARGET)/%)
+ROOTPYTHONVENDORBOOTMGMTTARGETCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTTARGET)/%)
+ROOTPYTHONVENDORBOOTMGMTTARGETLIBS=	$(CPYTHONLIBS:%=$(ROOTPYTHONVENDORMGMTTARGET)/%)
+
 DYNLIBLINK =	$(DYNLIB:%$(VERS)=%)
 COSDYNLIBLINK =	$(COSDYNLIB:%$(VERS)=%)
 
@@ -112,6 +116,10 @@
 $(ROOTPYTHONVENDORSOLINSTALLMODS):=	OWNER = root
 $(ROOTPYTHONVENDORSOLINSTALLMODS):=	GROUP = bin
 
+$(ROOTPYTHONVENDORBOOTMGMTMODS):=	FILEMODE = 444
+$(ROOTPYTHONVENDORBOOTMGMTMODS):=	OWNER = root
+$(ROOTPYTHONVENDORBOOTMGMTMODS):=	GROUP = bin
+
 LORDER=		lorder
 TSORT=		tsort
 AWK=		awk
@@ -178,6 +186,7 @@
 $(ROOTPYTHONVENDORINSTALLLIBS) := PNAME = pics
 $(ROOTPYTHONVENDORSOLINSTALLLIBS) := PNAME = pics
 $(ROOTPYTHONVENDORSOLINSTALLTARGETLIBS) := PNAME = pics
+$(ROOTPYTHONVENDORBOOTMGMTTARGETLIBS) := PNAME = pics
 
 # build rule for "portable" source
 objs/${ARCH}/%.o profs/${ARCH}/%.o pics/${ARCH}/%.o: %.c
--- a/usr/src/lib/Makefile.targ	Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/lib/Makefile.targ	Thu Apr 21 23:16:03 2011 -0600
@@ -151,6 +151,30 @@
 $(ROOTPYTHONVENDORINSTALLTRANSFER):
 	$(INS.dir)
 
+$(ROOTPYTHONVENDORBOOTMGMT):
+	$(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKND):
+	$(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN):
+	$(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS):
+	$(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC):
+	$(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86):
+	$(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDFW):
+	$(INS.dir)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER):
+	$(INS.dir)
+
 $(ROOTPYTHONTERMUI):
 	$(INS.dir)
 
@@ -206,6 +230,30 @@
 $(ROOTPYTHONVENDORINSTALLTRANSFER)/%: %
 	$(CP_P.file)
 
+$(ROOTPYTHONVENDORBOOTMGMT)/%: %
+	$(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKND)/%: %
+	$(CP_P.file)
+			      			  
+$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN)/%: %
+	$(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/%: %
+	$(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC)/%: %
+	$(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86)/%: %
+	$(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDFW)/%: %
+	$(CP_P.file)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER)/%: %
+	$(CP_P.file)
+
 $(ROOTPYTHONTERMUI)/%: %
 	$(CP_P.file)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,78 @@
+# 
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.lib
+
+SUBDIRS=	backend
+
+.PARALLEL:	$(SUBDIRS)
+
+all:=		TARGET= all
+check:=		TARGET= check
+clean:=		TARGET= clean
+clobber:=	TARGET= clobber
+install:=	TARGET= install
+install_h:=	TARGET= install_h
+lint:=		TARGET= lint
+
+all check clean clobber install install_h lint: $(SUBDIRS)
+
+PYMODS=	__init__.py \
+	bootarchive.py \
+	bootconfig.py \
+	bootinfo.py \
+	bootloader.py \
+	bootutil.py \
+	pysol.py
+
+PYCMODS=	$(PYMODS:%.py=%.pyc)
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMT)/%)
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMT)/%)
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+%.pyc : %.py
+	$(RM) $@
+	$(PYTHON) -mpy_compile $<
+	@[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+all:	$(SUBDIRS) $(PYCMODS) 
+
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS)  : $(ROOTPYTHONVENDORBOOTMGMT)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMT) $(PYCMODS)
+
+install:	all $(ROOTPYMODS) $(ROOTPYCMODS) $(SUBDIRS)
+
+install_h:
+
+$(SUBDIRS):	FRC
+	cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/__init__.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,85 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+A Python package for management of all things Boot
+"""
+
+class BootmgmtError(Exception):
+    def __init__(self, msg, xcpt=None):
+        self.msg = msg
+        self.xcpt = xcpt
+
+    def __str__(self):
+        if not self.xcpt is None:
+            return self.msg + '(' + str(self.xcpt) + ')'
+        else:
+            return self.msg
+
+class BootmgmtNotSupportedError(BootmgmtError):
+    pass
+
+class BootmgmtArgumentError(BootmgmtError):
+    pass
+
+class BootmgmtMissingInfoError(BootmgmtError):
+    pass
+
+class BootmgmtUnsupportedOperationError(BootmgmtError):
+    pass
+
+class BootmgmtMalformedPropertyNameError(BootmgmtError):
+    pass
+
+class BootmgmtMalformedPropertyValueError(BootmgmtError):
+    def __init__(self, propname, propval):
+        super(self, BootmgmtMalformedPropertyValueError).__init__(
+           'Invalid value specified for property "%s": %s' %
+           (str(propname), str(propval)))
+        self.propname = propname
+        self.propval = propval
+
+class BootmgmtReadError(BootmgmtError):
+    pass
+
+class BootmgmtWriteError(BootmgmtError):
+    pass
+
+class BootmgmtUnsupportedPlatformError(BootmgmtError):
+    pass
+
+class BootmgmtInterfaceCodingError(BootmgmtError):
+    pass
+
+class BootmgmtConfigReadError(BootmgmtError):
+    pass
+
+class BootmgmtConfigWriteError(BootmgmtError):
+    pass
+
+class BootmgmtIncompleteBootConfigError(BootmgmtError):
+    pass
+
+class BootmgmtUnsupportedPropertyError(BootmgmtUnsupportedOperationError):
+    pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,77 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../Makefile.lib
+
+SUBDIRS=	autogen \
+		bootvars \
+		fw \
+		loader
+
+.PARALLEL:	$(SUBRDIRS)
+
+all:=		TARGET=	all
+check:=		TARGET= check
+clean:=		TARGET=	clean
+clobber:=	TARGET=	clobber
+install:=	TARGET=	install
+install_h:=	TARGET= install_h
+lint:=		TARGET= lint
+
+all check clean clobber install install_h lint: $(SUBDIRS)
+
+PYMODS=	__init__.py 
+
+PYCMODS=	$(PYMODS:%.py=%.pyc)
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKND)/%)
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKND)/%)
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+%.pyc : %.py
+	$(RM) $@
+	$(PYTHON) -mpy_compile $<
+	@[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all:	$(SUBDIRS) $(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKND): $(ROOTPYTHONVENDORBOOTMGMT) 
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS)  : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKND) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS) $(SUBDIRS)
+
+install_h:
+
+$(SUBDIRS): FRC
+	cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/__init__.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,27 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Various backend interfaces for pybootmgmt
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/autogen/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,57 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+PYMODS=	__init__.py \
+	solaris.py
+
+PYCMODS=	$(PYMODS:%.py=%.pyc)
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN)/%)
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN)/%)
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+%.pyc : %.py
+	$(RM) $@
+	$(PYTHON) -mpy_compile $<
+	@[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all:	$(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN) : $(ROOTPYTHONVENDORBOOTMGMTBKND) 
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT) 
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS)  : $(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDAUTOGEN) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/autogen/__init__.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,42 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+BootInstance autogenerator backend interfaces for pybootmgmt
+"""
+
+import sys
+
+autogen_backends = [ 'solaris' ]
+
+class BootInstanceAutogenFactory(object):
+    @staticmethod
+    def autogen(bootconfig):
+        instance_list = []
+        for backend in autogen_backends:
+            modname = __name__ + '.' + backend
+            __import__(modname, level=0)
+            ns = sys.modules[modname]
+            instance_list += ns.autogenerate_boot_instances(bootconfig)
+        return instance_list
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/autogen/solaris.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,65 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+BootInstance autogenerator Solaris backend for pybootmgmt
+"""
+
+import libbe_py
+
+from ...bootconfig import BootConfig, SolarisDiskBootInstance
+from ...bootutil import LoggerMixin
+
+class SolarisBootInstanceAutogenerator(LoggerMixin):
+    pass
+
+def autogenerate_boot_instances(bootconfig):
+    sbia = SolarisBootInstanceAutogenerator()
+
+    # XXX - Handle bootconfig instances that have boot_class != disk
+    if bootconfig.boot_class != BootConfig.BOOT_CLASS_DISK:
+        raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+    # Use libbe_py to get the list of boot environments, then iterate
+    # over the list, creating a new SolarisDiskBootInstance for each
+    # Note that the title will just be the last portion of the bootfs
+    # (i.e. <pool>/ROOT/<title>)
+    retcode, belist = libbe_py.beList()
+    if retcode != 0:
+        sbia._debug('libbe_py.beList() failed; return code was ' + str(retcode))
+        return []
+
+    inst_list = []
+    for bootenv in belist:
+        if bootenv.get('orig_be_name', None) is None:
+            continue  # skip over snapshots
+        bootinst = SolarisDiskBootInstance(None, title=bootenv['orig_be_name'],
+                                           bootfs=bootenv['root_ds'])
+        if bootenv['active'] is True:
+            sbia._debug('default boot instance is:\n' + str(bootinst))
+            bootinst.default = True
+
+        inst_list.append(bootinst)
+
+    return inst_list
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,75 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+SUBDIRS=	sparc \
+		x86
+
+.PARALLEL:	$(SUBRDIRS)
+
+all:=		TARGET=	all
+check:=		TARGET= check
+clean:=		TARGET=	clean
+clobber:=	TARGET=	clobber
+install:=	TARGET=	install
+install_h:=	TARGET= install_h
+lint:=		TARGET= lint
+
+all check clean clobber install install_h lint: $(SUBDIRS)
+
+PYMODS=	__init__.py 
+
+PYCMODS=	$(PYMODS:%.py=%.pyc)
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/%)
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)/%)
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+%.pyc : %.py
+	$(RM) $@
+	$(PYTHON) -mpy_compile $<
+	@[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all:	$(SUBDIRS) $(PYCMODS)
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS) : $(ROOTPYTHONVENDORBOOTMGMTBKND)
+$(ROOTPYTHONVENDORBOOTMGMTBKND): $(ROOTPYTHONVENDORBOOTMGMT) 
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS)  : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARS) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS) $(SUBDIRS)
+
+install_h:
+
+$(SUBDIRS): FRC
+	cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/__init__.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,47 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Boot variables backend support for pybootmgmt
+"""
+
+import sys
+from ... import bootutil
+from ... import BootmgmtArgumentError
+
+class BackendBootVarsFactory(object):
+    @staticmethod
+    def get(sysroot, arch, osname):
+        """Returns an instance of bootinfo.BootVariables corresponding to the
+        architecture and system root passed in."""
+        if arch is None:
+            arch = bootutil.get_current_arch_string()
+        if osname is None:
+            raise BootmgmtArgumentError('osname cannot be None')
+
+        bvmod = __name__ + '.' + arch + '.' + osname
+        __import__(bvmod, level=0)
+        ns = sys.modules[bvmod]
+        # bootvars_backend returns the *class*
+        return ns.bootvars_backend()(sysroot)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/sparc/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,57 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../../Makefile.lib
+
+PYMODS=	__init__.py \
+	solaris.py
+
+PYCMODS=	$(PYMODS:%.py=%.pyc)
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC)/%)
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC)/%)
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+%.pyc : %.py
+	$(RM) $@
+	$(PYTHON) -mpy_compile $<
+	@[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all:	$(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC) : $(ROOTPYTHONVENDORBOOTMGMTBKND) 
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT) 
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS)  : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSSPARC) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/sparc/__init__.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,27 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+SPARC Boot variables backend support for pybootmgmt
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/sparc/solaris.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,36 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+SPARC Solaris Boot variables backend support for pybootmgmt
+"""
+
+import sys
+from .... import bootinfo
+
+class OBPBootVariables(bootinfo.BootVariables):
+    pass
+
+def bootvars_backend():
+    return OBPBootVariables
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/x86/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,57 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../../Makefile.lib
+
+PYMODS=	__init__.py \
+	solaris.py
+
+PYCMODS=	$(PYMODS:%.py=%.pyc)
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86)/%)
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86)/%)
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+%.pyc : %.py
+	$(RM) $@
+	$(PYTHON) -mpy_compile $<
+	@[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all:	$(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86) : $(ROOTPYTHONVENDORBOOTMGMTBKND) 
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT) 
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS)  : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDBOOTVARSX86) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/x86/__init__.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,27 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+x86 Boot variables backend support for pybootmgmt
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/bootvars/x86/solaris.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,216 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+x86 Solaris Boot variables backend support for pybootmgmt
+"""
+
+import tempfile
+import sys
+import logging
+import shutil
+from .... import bootinfo
+from .... import BootmgmtMalformedPropertyNameError, BootmgmtArgumentError
+from .... import BootmgmtReadError, BootmgmtWriteError
+
+logger = logging.getLogger('bootmgmt')
+
+class BootenvBootVariables(bootinfo.BootVariables):
+    """This class supports manipulation of boot variables stored in the
+    <root>/boot/solaris/bootenv.rc file."""
+
+    BOOTENV_RC = '/boot/solaris/bootenv.rc'
+
+    def __init__(self, sysroot=None):
+        self.BOOTENV_DOT_RC = sysroot + BootenvBootVariables.BOOTENV_RC
+        self._dirty = False
+        super(BootenvBootVariables, self).__init__(sysroot)
+
+    @property
+    def dirty(self):
+        return self._dirty
+
+    @dirty.setter
+    def dirty(self, value):
+        if not type(value) is bool:
+            raise ValueError('dirty is a bool')
+        if self._dirty != value:
+            logger.debug(self.__class__.__name__ + ': dirty => %s' % str(value))
+            self._dirty = value
+
+    def setprop(self, propname, value):
+        # if the property name contains whitespace, it's invalid
+        if len(set(propname).intersection(' \t')) > 0:
+            raise BootmgmtMalformedPropertyNameError('Invalid property name' +
+                                                     ' ("%s")' % propname)
+        if value is None: # Not allowed -- there must be a real value here
+            raise BootmgmtArgumentError('value must not be None')
+
+        if propname in self._vardict:
+            if self._vardict[propname][1] != value:
+                # All we need to do is update the value portion of the list
+                # (this automatically "updates" the value stored in _rawlines)
+                self._vardict[propname][1] = value
+                self.dirty = True
+        else:
+            proplist = [propname, value]
+            # The _vardict must use the same object that's added to _rawlines
+            # so that updates are seamless across both containers
+            self._vardict[propname] = proplist
+            self._rawlines.append(proplist)
+            self.dirty = True
+
+        
+
+    def getprop(self, propname):
+        val_list = self._vardict.get(propname, None)
+        # The values stored in the dictionary are 2-element lists
+        # The first element is the prop name and the second is the value
+        if not val_list is None and len(val_list) == 2:
+            return val_list[1]
+        else:
+            return None
+
+    def delprop(self, propname):
+        if propname in self._vardict:
+            # Clear the list first so that _rawlines is "updated" to contain
+            # a 0-length list for this property
+            del self._vardict[propname][:]
+            # Now remove the property from the _vardict dict
+            del self._vardict[propname]
+            self.dirty = True
+
+    def _read(self):
+        """Reads the set of properties from the bootenv.rc file under
+           sysroot.  Keeps a copy of the file in memory so comments can
+           be preserved."""
+        bvfile = self.BOOTENV_DOT_RC
+        self._rawlines = []
+        self._vardict = {}
+        try:
+            with open(bvfile) as berc:
+                for rawline in berc:
+                    nextline = rawline.strip()
+                    # skip comment lines
+                    if len(nextline) > 0 and nextline[0] != '#':
+                        # Store the property in the _rawlines list as a
+                        # list so that we can make changes via the _vardict
+                        # The form of a line is:
+                        # setprop <propname> <propval>
+                        # If we find a line that's malformed, ignore it and
+                        # continue
+                        try:
+                            keyword, prop, val = nextline.split(None, 2)
+                        except ValueError:
+                            logger.debug('Malformed line in ' + bvfile +
+                                         ': "%s"' % nextline)
+                            continue
+
+                        if keyword != 'setprop':
+                            logger.debug('Malformed line in ' + bvfile +
+                                         ': "%s"' % nextline)
+                            continue
+
+                        newbep = [prop, val]
+                        self._rawlines.append(newbep)
+                        self._vardict[prop] = newbep
+                    else:
+                        self._rawlines.append(rawline)
+
+                    
+        except IOError as e:
+            raise BootmgmtReadError('Error while loading boot variables ' +
+                                    'from ' + bvfile, e)
+
+    def write(self, inst, alt_dir=None):
+        # Open a new file that will contain the new bootenv.rc, dump all
+        # the variables to that file, then move that file over to be the
+        # real bootenv.rc
+
+        if not alt_dir is None:
+            try:
+                fileobj = tempfile.NamedTemporaryFile(dir=alt_dir, delete=False)
+            except IOError as err:
+                raise BootmgmtWriteError('Error while writing to temporary ' +
+                                         'bootenv.rc (%s)' % fileobj.name, err)
+            bvfile = fileobj.name
+            bvtempfile = bvfile
+        else:
+            # BOOTENV_DOT_RC has a leading slash:
+            bvfile = inst.rootpath + self.BOOTENV_DOT_RC
+            bvtempfile = bvfile + '.new'
+            try:
+                fileobj = open(bvtempfile, 'w')
+            except IOError as err:
+                raise BootmgmtWriteError('Error while writing to temporary ' +
+                                         'bootenv.rc (%s)' % bvtempfile, err)
+
+        try:
+            with fileobj as berc:
+                # Write each line to the output file -- if the item in
+                # _rawlines is a list, construct a setprop command string,
+                # otherwise, just copy it verbatim
+                for line in self._rawlines:
+                    if type(line) is list and len(line) == 2:
+                        berc.write('setprop ' + line[0] + ' ' + line[1] + '\n')
+                    elif type(line) is str:
+                        berc.write(line) # newline is already part of line
+
+        except IOError as err:
+            raise BootmgmtWriteError('Error while writing to temporary ' +
+                                     'bootenv.rc (%s)' % bvtempfile, err)
+
+        # Now move the file over the become the new bootenv.rc:
+        try:
+            if not alt_dir is None:
+                shutil.move(bvtempfile, bvfile)
+            self.dirty = False
+        except IOError as ioe:
+            try:
+                # Try to clean up by deleting the temporary file
+                os.remove(bvtempfile)
+            except OSError as ose:
+                logger.debug("Couldn't clean up temporary bootenv.rc: " +
+                             ose.strerror)
+                pass
+            raise BootmgmtWriteError('Error while moving the temporary ' +
+                                     'bootenv.rc (%s) to %s' %
+                                     (bvtempfile, bvfile), ioe)
+
+        if not alt_dir is None:
+            return ('file', bvfile, inst,
+                    '%(systemroot)s' + BootenvBootVariables.BOOTENV_RC,
+                    'root', 'sys', 0644)
+        else:
+            return None
+
+    def __len__(self):
+        return len(self._vardict)
+
+    def __iter__(self):
+        classic_dict = [(x, z) for (x,[y,z]) in self._vardict.items()]
+        return classic_dict.__iter__()            
+
+def bootvars_backend():
+    return BootenvBootVariables
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,59 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+PYMODS=	__init__.py \
+	bios.py \
+	obp.py \
+	uefi64.py
+
+PYCMODS=	$(PYMODS:%.py=%.pyc)
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDFW)/%)
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDFW)/%)
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+%.pyc : %.py
+	$(RM) $@
+	$(PYTHON) -mpy_compile $<
+	@[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all:	$(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDFW) : $(ROOTPYTHONVENDORBOOTMGMTBKND) 
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT) 
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS)  : $(ROOTPYTHONVENDORBOOTMGMTBKNDFW)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDFW) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/__init__.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,76 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+System firmware backend support for pybootmgmt
+"""
+
+from ...bootutil import get_current_arch_string
+from ... import BootmgmtUnsupportedPlatformError
+from ... import pysol
+import sys
+
+class BackendFWFactory(object):
+    @staticmethod
+    def get(fw_name):
+        """Returns an instance of bootinfo.SystemFirmware corresponding to the
+        firmware name passed in.  If fw_name is None, the system firmware type
+        is autodetected and the appropriate child of SystemFirmware is returned
+        """
+        if fw_name is None:
+            curarch = get_current_arch_string()
+            # If this is a SPARC system, the appropriate class is obp
+            if curarch == 'sparc':
+                from . import obp
+                return obp.firmware_backend()('obp')
+            elif curarch == 'x86':
+            # If this is an x86 system and the efi-systype property exists,
+            # then this is a UEFI system and the property value specifies the
+            # bit width.
+                try:
+                    efisystype = pysol.di_find_root_prop('efi-systype')
+                except IOError as e:
+                    # Problem while trying to get the property
+                    # Set efisystype to None to force BIOS
+                    efisystype = None
+
+                if efisystype is None:
+                    from . import bios
+                    return bios.firmware_backend()('bios')
+                else:
+                    uefi_string = 'uefi' + efisystype
+                    fwmod = __name__ + '.' + uefi_string
+                    __import__(fwmod, level=0)
+                    ns = sys.modules[fwmod]
+                    return ns.firmware_backend()(uefi_string)
+            else:
+                raise BootmgmtUnsupportedPlatformError('Unknown platform '
+                       '"%s"' % curarch)
+        else:
+            fwmod = __name__ + '.' + fw_name
+            __import__(fwmod, level=0)
+            ns = sys.modules[fwmod]
+            return ns.firmware_backend()(fw_name)
+        
+        
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/bios.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,50 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+x86 BIOS firmware backend for pybootmgmt
+"""
+
+from ... import bootinfo
+from ... import BootmgmtUnsupportedOperationError, BootmgmtWriteError
+
+class BIOSFirmware(bootinfo.SystemFirmware):
+    def getprop(self, propname):
+        return super(BIOSFirmware, self).getprop(propname)        
+
+    def setprop(self, propname, value):
+        """Setting properties is not supported on BIOS systems"""
+        if propname == SystemFirmware.PROP_BOOT_DEVICE:
+            raise BootmgmtWriteError('Properties are read-only on systems '
+              'with BIOS firmware')
+        raise BootmgmtUnsupportedOperationError('Properties cannot be set '
+              'on systems with BIOS firmware')
+
+    def delprop(self, propname):
+        """Deleting properties is not supported on BIOS systems"""
+        raise BootmgmtUnsupportedOperationError('Properties cannot be '
+              'deleted on systems with BIOS firmware')
+
+def firmware_backend():
+    return BIOSFirmware
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/obp.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,35 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+OpenBoot Firmware firmware backend for pybootmgmt
+"""
+
+from ... import bootinfo
+
+class OBPFirmware(bootinfo.SystemFirmware):
+    pass
+
+def firmware_backend():
+    return OBPFirmware
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/fw/uefi64.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,35 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+UEFI firmware backend for pybootmgmt (only 64-bit UEFI is supported)
+"""
+
+from ... import bootinfo
+
+class UEFI64Firmware(bootinfo.SystemFirmware):
+    pass
+
+def firmware_backend():
+    return UEFI64Firmware
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,60 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+PYMODS=	__init__.py \
+	grub2.py \
+	legacygrub.py \
+	menulst.py \
+	sbb.py
+
+PYCMODS=	$(PYMODS:%.py=%.pyc)
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER)/%)
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER)/%)
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+%.pyc : %.py
+	$(RM) $@
+	$(PYTHON) -mpy_compile $<
+	@[ $(<)c = $@ ] || $(MV) $(<)c $@
+
+
+all:	$(PYCMODS)
+
+$(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER) : $(ROOTPYTHONVENDORBOOTMGMTBKND) 
+$(ROOTPYTHONVENDORBOOTMGMTBKND) : $(ROOTPYTHONVENDORBOOTMGMT) 
+$(ROOTPYTHONVENDORBOOTMGMT) : $(ROOTPYTHONVENDOR)
+
+$(ROOTPYMODS)  : $(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER)
+$(ROOTPYCMODS) : $(ROOTPYTHONVENDORBOOTMGMTBKNDLOADER) $(PYCMODS)
+
+install: all $(ROOTPYMODS) $(ROOTPYCMODS)
+
+install_h:
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/__init__.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,68 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Various bootloader interfaces for pybootmgmt
+"""
+
+import sys
+from ...bootutil import LoggerMixin
+
+boot_loader_backends = [ 'grub2', 'legacygrub', 'sbb' ]
+
+class BackendBootLoaderFactory(LoggerMixin):
+    @classmethod
+    def get(cls, bootconfig):
+        """Returns an instance of bootloader.BootLoader appropriate for the
+        system identified by the keyword arguments passed in.  Invokes a
+        probe function for each boot loader backend, and returns the loader
+        whose probe function returned the highest weight value.  If multiple
+        boot loaders' probe functions succeed, a reference to the
+        lower-weighted (deprecated) loader is stored in the higher-weighted
+        loader's old_loader member (only two loaders can be linked in this
+        manner).
+        """
+        loaderlist = []
+        loader_instance = None
+
+        for loader in boot_loader_backends:
+            blmod = __name__ + '.' + loader
+            __import__(blmod, level=0)
+            ns = sys.modules[blmod]
+            for loaderclass in ns.bootloader_classes():
+                loaderinst, loaderwt = loaderclass.probe(bootconfig=bootconfig)
+                if not loaderinst is None:
+                    loaderlist.append((loaderinst, loaderwt))
+
+        # Sort the loader list by weight:
+        loaderlist.sort(key=(lambda t: t[1]))
+
+        cls._debug('loaderlist => ' + str(loaderlist))
+
+        if len(loaderlist) > 0:
+            loader_instance = loaderlist[-1][0]
+        if len(loaderlist) > 1:
+            loader_instance.old_loader = loaderlist[-2][0]
+
+        return loader_instance        
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/grub2.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,37 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+GRUB2 boot loader backend for pybootmgmt
+"""
+
+from ...bootloader import BootLoader
+
+class GRUB2BootLoader(BootLoader):
+    @classmethod
+    def probe(cls, **kwargs):
+        return (None, None)
+
+def bootloader_classes():
+    return [GRUB2BootLoader]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/legacygrub.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,945 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Legacy GRUB BootLoader Implementation for pybootmgmt
+"""
+
+import tempfile
+import shutil
+import os
+import pwd
+import grp
+import gettext
+import stat
+import re
+
+from .menulst import MenuDotLst, MenuLstError, MenuLstMenuEntry, MenuLstCommand
+from ...bootloader import BootLoader, BootLoaderInstallError
+from ...bootconfig import BootConfig, DiskBootConfig, SolarisDiskBootInstance
+from ...bootconfig import ChainDiskBootInstance
+from ... import BootmgmtArgumentError
+from ... import BootmgmtUnsupportedOperationError, BootmgmtInterfaceCodingError
+from ... import BootmgmtIncompleteBootConfigError, BootmgmtConfigReadError
+from ... import BootmgmtConfigWriteError, BootmgmtMalformedPropertyValueError
+from ... import BootmgmtUnsupportedPlatformError, BootmgmtNotSupportedError
+from solaris_install import Popen, CalledProcessError
+
+_ = gettext.translation("SUNW_OST_OSCMD", "/usr/lib/locale",
+    fallback=True).gettext
+
+class LegacyGRUBBootLoader(BootLoader):
+    """Implementation of a Legacy GRUB (GRUB 0.97) BootLoader.  Handles parsing
+    the menu.lst file (reading and writing), though reading it and creating
+    BootInstance objects is rather fragile"""
+
+    WEIGHT = 1  # Legacy GRUB's probe weight
+
+    MENU_LST_PATH = '/boot/grub/menu.lst'
+
+    DEFAULT_PROPDICT = {
+         'default_command'     : 'default 0',
+         'timeout_command'     : 'timeout 10',
+         'serial_command'      : '#   serial --unit=0 --speed=9600',
+         'terminal_command'    : '#   terminal serial',
+         'splashimage_command' : '#   splashimage /boot/grub/splash.xpm.gz',
+         # if foreground or background are used, they MUST start with
+         # a newline (see the MENU_LST_PREAMBLE, below)
+         'foreground'          : '',
+         'background'          : '',
+         'minmem64'            : '',
+         'hidemenu'            : '' }
+
+    INSTALLGRUB_NOUPDT = 4       # from src/cmd/boot/common/boot_utils.h
+    INSTALLGRUB_NOEINFO = 6      # (ditto)
+
+    DEFAULT_TIMEOUT = 10         # 10 seconds is the default timeout
+    DEFAULT_FORECOLOR = '343434'
+    DEFAULT_BACKCOLOR = 'F7FBFF'
+
+    # Supported properties for setprop()
+    SUPPORTED_PROPS = [BootLoader.PROP_CONSOLE,
+                       BootLoader.PROP_SERIAL_PARAMS,
+                       BootLoader.PROP_MINMEM64,
+                       BootLoader.PROP_TIMEOUT,
+                       BootLoader.PROP_QUIET]
+
+    MENU_LST_PREAMBLE = (
+r"""# default menu entry to boot
+%(default_command)s
+#
+# menu timeout in second before default OS is booted
+# set to -1 to wait for user input
+%(timeout_command)s
+#
+# To enable grub serial console to ttya uncomment the following lines
+# and comment out the splashimage line below
+# WARNING: do not enable grub serial console when BIOS console serial
+#       redirection is active.
+%(serial_command)s
+%(terminal_command)s
+#
+# Uncomment the following line to enable GRUB splashimage on console
+%(splashimage_command)s%(foreground)s%(background)s
+#
+# To chainload another OS
+#
+# title Another OS
+#       root (hd<disk no>,<partition no>)
+#       chainloader +1
+#
+# To chainload a Solaris release not based on grub
+#
+# title Solaris 9
+#       root (hd<disk no>,<partition no>)
+#       chainloader +1
+#       makeactive
+#
+# To load a Solaris instance based on grub
+# If GRUB determines if the booting system is 64-bit capable,
+# the kernel$ and module$ commands expand $ISADIR to "amd64"
+#
+# title Solaris <version>
+#       findroot (pool_<poolname>,<partition no>,x)   --x = Solaris root slice
+#       bootfs <poolname>/ROOT/<BE_name>
+#       kernel$ /platform/i86pc/kernel/$ISADIR/unix
+#       module$ /platform/i86pc/$ISADIR/boot_archive
+
+#
+# To override Solaris boot args (see kernel(1M)), console device and
+# properties set via eeprom(1M) edit the "kernel" line to:
+#
+#   kernel /platform/i86pc/kernel/unix <boot-args> -B prop1=val1,prop2=val2,...
+#
+%(hidemenu)s
+%(minmem64)s
+""")
+
+    @classmethod
+    def probe(cls, **kwargs):
+        """Probe for Legacy GRUB files for use with the BootConfig passed
+        in"""
+
+        bootconfig = kwargs.get('bootconfig', None)
+
+        if (bootconfig is None or bootconfig.boot_class is None):
+            return (None, None)
+
+        if (bootconfig.boot_class == BootConfig.BOOT_CLASS_DISK and
+           not bootconfig.boot_fstype is None):
+            return LegacyGRUBBootLoader._probe_disk(**kwargs)
+        elif bootconfig.boot_class == BootConfig.BOOT_CLASS_ODD:
+            return LegacyGRUBBootLoader._probe_odd(**kwargs)
+        else:
+            raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+    @classmethod
+    def _probe_odd(cls, **kwargs):
+        """This Legacy GRUB probe function searches the ODD's root, looking for
+        the Legacy GRUB menu.lst and stage1 and stage2 files"""
+
+        bootconfig = kwargs.get('bootconfig', None)
+
+        root = bootconfig.get_root()
+
+        cls._debug('_probe_odd(): odd_image_root=%s' % root)
+
+        try:
+            cls._probe_generic(root, root,
+                               ['stage2_eltorito'])
+        except BootmgmtNotSupportedError:
+            return (None, None)
+
+        return (LegacyGRUBBootLoader(**kwargs), LegacyGRUBBootLoader.WEIGHT)
+
+    @classmethod
+    def _probe_disk(cls, **kwargs):
+        """This Legacy GRUB probe function searches the ZFS top-level dataset
+        for a menu.lst file.  If that's not present, we search the system root
+        for /boot/grub/stage1 and /boot/grub/stage2 -- essential files that
+        should exist if Legacy GRUB is the active boot loader.
+        """
+ 
+        bootconfig = kwargs.get('bootconfig', None)
+
+        fstype = bootconfig.boot_fstype
+
+        if fstype == 'ufs':
+            menuroot = bootconfig.get_root()
+            dataroot = menuroot
+        elif fstype == 'zfs':
+            menuroot = bootconfig.zfstop
+            dataroot = bootconfig.get_root()
+        else:
+            return (None, None)
+
+        cls._debug('_probe_disk():'
+                   ' menuroot=%s, dataroot=%s' % (menuroot, dataroot))
+
+        try:
+            cls._probe_generic(menuroot, dataroot,
+                               ['stage1', 'stage2'])
+        except BootmgmtNotSupportedError:
+            return (None, None)
+
+        # XXX - In addition to the loader files themselves, we need to ensure
+        # XXX - that we have access to the installgrub program in the currently-
+        # XXX - running system (otherwise, we'll have no way to install Legacy
+        # XXX - GRUB).
+
+        return (LegacyGRUBBootLoader(**kwargs), LegacyGRUBBootLoader.WEIGHT)
+
+    @classmethod
+    def _probe_generic(cls, menuroot, dataroot, datafiles):
+
+        # Both the menu root and the data root locations must be specified
+        if menuroot is None or dataroot is None:
+            raise BootmgmtNotSupportedError('menuroot or dataroot is None')
+
+        menulst = menuroot + LegacyGRUBBootLoader.MENU_LST_PATH
+        try:
+            open(menulst).close()
+        except IOError as ioerr:
+            cls._debug(('Error opening %s: ' % menulst) + ioerr.strerror)
+
+        try:
+            for datafile in datafiles:
+                open(dataroot + '/boot/grub/' + datafile).close()
+        except IOError as ioerr:
+            cls._debug(str(ioerr))
+            raise BootmgmtNotSupportedError('IOError when checking for '
+                                            'datafiles')
+
+    def _serial_parameters(self):
+        """Parse serial parameters and return a tuple of (serial port number,
+        serial port speed, data bits, parity (one of 'no', 'odd' or 'even'),
+        stop bits (0 or 1)) (all tuple members must be strings).
+
+        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 valid 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,9600,8,'N',1,None).
+        """
+
+        params = self.getprop('serial_params')
+
+        if not params is None:
+            if not params[BootLoader.PROP_SP_PARITY] is None:
+                try:
+                    parity = {'N' : 'no',
+                              'E' : 'even',
+                              'O' : 'odd'}[params[BootLoader.PROP_SP_PARITY]]
+                except KeyError:
+                    self._debug('Bad parity value in serial_params')
+                    parity = None
+
+            ret_params = (params[BootLoader.PROP_SP_PORT],
+                          params[BootLoader.PROP_SP_SPEED],
+                          params[BootLoader.PROP_SP_DATAB],
+                          parity,
+                          params[BootLoader.PROP_SP_STOPB])
+
+            # if port is not indicated, force use of port 0.
+            if ret_params[0] is None:
+                ret_params[0] = '0'
+
+            return ret_params
+            
+        return ('0', '9600', None, None, None)
+
+    def __init__(self, **kwargs):
+        self.pkg_names = [ 'system/boot/grub', 'SUNWgrub' ]
+        self.name = 'Legacy GRUB'
+        self._menufile = None
+        super(LegacyGRUBBootLoader, self).__init__(**kwargs)
+        self._bl_props[BootLoader.PROP_BOOT_TARGS] = 'bios'
+
+    def new_config(self):
+        """The configuration for Legacy GRUB consists solely of the menu.lst
+        file.  The default new configuration is an empty menu.lst file,
+        with a graphical splashscreen and appropriate fore/back colors."""
+        self._bl_props[BootLoader.PROP_CONSOLE] = BootLoader.PROP_CONSOLE_GFX
+
+    def load_config(self):
+        """Load boot instances and GRUB properties from the menu.lst file"""
+        if self._boot_config.boot_class == BootConfig.BOOT_CLASS_DISK:
+            self._load_config_disk()
+            self.dirty = False  # We just loaded a clean config from disk!
+        else:
+            raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+    def _write_config(self, basepath):
+        """The only file that needs to be written is the menu.lst file, using
+        information from the BootConfig instance to which we have a reference.
+        Information from the boot loader's properties is also used to
+        determine which commands are emitted at the global level"""
+
+        if self._boot_config is None:
+            msg = ('Cannot _write_config(%s) - _boot_config is None' %
+                  str(basepath))
+            self._debug(msg)
+            raise BootmgmtInterfaceCodingError(msg)
+
+        # Determine the type of boot configuration we're dealing with, then
+        # determine the filesystem type that will hold the menu.lst.  Only
+        # then can we tell the caller the appropriate path to copy it into.
+        if self._boot_config.boot_class == BootConfig.BOOT_CLASS_DISK:
+            return self._write_config_disk(basepath)
+        elif self._boot_config.boot_class == BootConfig.BOOT_CLASS_ODD:
+            return self._write_config_odd(basepath)
+        else:
+            raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+    # Legacy GRUB methods for dealing with a disk-based BootConfig
+    def _menu_lst_dir_disk(self):
+        fstype = self._boot_config.boot_fstype
+        if fstype != 'zfs' and fstype != 'ufs':
+            raise BootmgmtUnsupportedOperationError('Unknown filesystem: %s'
+                                                     % fstype)
+        if fstype == 'zfs':
+            menu_lst_dir = self._boot_config.zfstop
+        elif fstype == 'ufs':
+            menu_lst_dir = self._boot_config.get_root()
+
+        return menu_lst_dir
+
+    def _load_config_disk(self):
+
+        menu_lst = (self._menu_lst_dir_disk() +
+                   LegacyGRUBBootLoader.MENU_LST_PATH)
+        try:
+            self._menufile = LegacyGRUBMenuFile(menu_lst)
+        except IOError as err:
+            raise BootmgmtConfigReadError('Error while processing the %s file' %
+                                          menu_lst, err)
+        except MenuLstError as err:
+            raise BootmgmtConfigReadError('Error while processing the %s '
+                                          'file: %s' % (menu_lst, str(err)))
+
+        default_index = None
+        boot_instances = []
+        # Extract the properties and entries from the parsed file:
+        for entity in self._menufile.entities():
+            if isinstance(entity, MenuLstMenuEntry):
+                # Make sure we have everything first:
+                # XXX - Recognize non-disk boot instances in the menu.lst
+                argdict = {}
+                if entity.find_command('bootfs'):
+                    for cmd in entity.commands():
+                        # Skip non-commands
+                        if not isinstance(cmd, MenuLstCommand):
+                            continue
+
+                        if cmd.get_command() == 'title':
+                            argdict['title'] = ' '.join(cmd.get_args())
+                        elif ((cmd.get_command() == 'kernel$' or
+                            cmd.get_command() == 'kernel') and
+                            len(cmd.get_args()) > 0):
+                            argdict['kernel'] = cmd.get_args()[0]
+                            argdict['kargs'] = ' '.join(cmd.get_args()[1:])
+                        elif (cmd.get_command() == 'module$' or
+                              cmd.get_command() == 'module'):
+                            argdict['boot_archive'] = ' '.join(cmd.get_args())
+                        elif cmd.get_command() == 'bootfs':
+                            argdict['bootfs'] = ' '.join(cmd.get_args())
+                            argdict['fstype'] = 'zfs'
+                        elif cmd.get_command() == 'findroot':
+                            # XXX - Handle "BE_XXXX" and other hints
+                            argstring = ''.join(cmd.get_args())
+                            (pool, subs) = re.subn(
+                                        r'.*pool_([^,)]+).*', r'\1', argstring)
+                            if subs > 0:
+                                argdict['signature'] = 'pool_' + pool
+                                argdict['rpool'] = pool
+                                argdict['fstype'] = 'zfs'
+
+                elif entity.find_command('chainloader'):
+                    for cmd in entity.commands():
+                        if cmd.get_command() == 'title':
+                            argdict['title'] = ' '.join(cmd.get_args())
+                        elif (cmd.get_command() == 'root' or
+                            cmd.get_command() == 'rootnoverify'):
+                            argdict['root'] = ' '.join(cmd.get_args())
+                        elif cmd.get_command() == 'chainloader':
+                            argdict['chainload'] = ' '.join(cmd.get_args())
+
+                elif entity.find_command('findroot'):
+                    # XXX - Look up ZFS bootfs property for the pool
+                    raise BootmgmtUnsupportedOperationError('XXX - Fix Me')
+
+                if not argdict.get('chainload', None) is None:
+                    inst = ChainDiskBootInstance(None, **argdict)
+                else:
+                    inst = SolarisDiskBootInstance(None, **argdict)
+
+                boot_instances += [inst]
+
+            elif isinstance(entity, MenuLstCommand) is True:
+                # Add the command as a property
+                arglist = entity.get_args()
+                if not arglist is None and len(arglist) > 0:
+                    argstring = ' '.join(arglist)
+                else:
+                    argstring = ''
+                # XXX - Don't use setprop here; if the entity contains a
+                # XXX - command we don't recognize, we'll get an exception
+                # XXX - instead, just record that command as-is so it can
+                # XXX - be replayed when the menu.lst is written
+                # XXX - In any case, we need to parse the command and turn it
+                # XXX - into a valid property key/value.
+                if entity.get_command() == 'default':
+                    default_index = argstring
+                else:
+                    self.setprop(entity.get_command(), argstring)
+
+        if not default_index is None:
+            try:
+                default_index = int(default_index)
+                self._debug('default GRUB entry is: ' + str(default_index))
+            except ValueError:
+                self._debug("Could not convert `default' index (%s) to "
+                            "an int -- setting to 0" % default_index)
+                default_index = 0
+            if default_index < len(self._boot_config.boot_instances):
+                boot_instances[default_index].default = True
+        elif len(boot_instances) > 0:
+            # Set the default to be the first one
+            boot_instances[0].default = True
+
+        # Add the boot instances to the BootConfig instance:
+        self._boot_config.add_boot_instance(boot_instances)
+
+    def _write_config_disk(self, basepath):
+        """This is a disk-based configuration.  We support ZFS or UFS root,
+        so figure out which filesystem we're dealing with, and write the
+        menu.lst file to that location."""
+
+        fstype = self._boot_config.boot_fstype
+        if fstype != 'zfs' and fstype != 'ufs':
+            raise BootmgmtUnsupportedOperationError('Unknown filesystem: %s'
+                                                     % fstype)
+        if fstype == 'zfs':
+            menu_lst_dir = self._boot_config.zfstop
+        elif fstype == 'ufs':
+            menu_lst_dir = self._boot_config.get_root()
+
+        tuples = self._write_config_generic(basepath, menu_lst_dir)
+
+        if tuples is None:
+            self._debug('No tuples returned from _write_config_generic')
+            return None
+
+        for idx, item in enumerate(tuples):
+            if (item[BootConfig.IDX_FILETYPE] is BootConfig.OUTPUT_TYPE_FILE and
+               item[BootConfig.IDX_DESTNAME] ==
+                                            LegacyGRUBBootLoader.MENU_LST_PATH):
+                # Make a copy of the tuple so we can change it:
+                item = list(item)
+
+                if fstype == 'zfs':
+                    item[BootConfig.IDX_DESTNAME] = (
+                        '%(' + DiskBootConfig.TOKEN_ZFS_RPOOL_TOP_DATASET + ')s'
+                        + LegacyGRUBBootLoader.MENU_LST_PATH)
+                elif fstype == 'ufs':
+                # The BootInstance included in the 6-tuple will be the first
+                # SolarisDiskBootInstance in the list held in the associated
+                # BootConfig's boot_instances list (this may need to be
+                # revisited).
+                    inst = None
+                    for inst in self._boot_config.boot_instances:
+                        if isinstance(inst, SolarisDiskBootInstance) is True:
+                            break
+
+                    item[BootConfig.IDX_DESTNAME] = (
+                                    '%(' + BootConfig.TOKEN_SYSTEMROOT + ')s' +
+                                    LegacyGRUBBootLoader.MENU_LST_PATH)
+                    item[BootConfig.IDX_INSTANCE] = inst
+
+                # Update item in the list:
+                item = tuple(item)
+                tuples[idx] = item
+
+        return tuples
+
+    def _write_config_generic(self, basepath, menu_lst_dir):
+
+        # If basepath is not None, the menu.lst should be written to a file
+        # under basepath (instead of to the actual location)
+        if basepath is None:
+            realmenu = menu_lst_dir + LegacyGRUBBootLoader.MENU_LST_PATH
+            tempmenu = realmenu + '.new'
+
+            try:
+                # Don't open the new menu.lst over the old -- create a
+                # temporary file, then, if the write is successful, move the
+                # temporary file over the old one.
+                outfile = open(tempmenu, 'w')
+                self._write_menu_lst(outfile)
+                outfile.close()
+            except IOError as err:
+                raise BootmgmtConfigWriteError("Couldn't write to %s" %
+                                               tempmenu, err)
+
+            try:
+                shutil.move(tempmenu, realmenu)
+            except IOError as err:
+                try:
+                    os.remove(tempmenu)
+                except OSError as oserr:
+                    self._debug('Error while trying to remove %s: %s' %
+                                (tempmenu, oserr.strerror))
+                raise BootmgmtConfigWriteError("Couldn't move %s to %s" %
+                                               (tempmenu, realmenu), err)
+
+            # Move was successful, so now set the owner and mode properly:
+            try:
+                os.chmod(realmenu, 0644)
+                os.chown(realmenu, pwd.getpwnam('root').pw_uid,
+                         grp.getgrnam('root').gr_gid)
+            except OSError as oserr:
+                raise BootmgmtConfigWriteError("Couldn't set mode/perms on "
+                      + realmenu, oserr)
+
+            return None
+
+        # basepath is set to a path.  Use it to form the path to a temporary
+        # file
+        try:
+            tmpfile = tempfile.NamedTemporaryFile(dir=basepath, delete=False)
+            self._write_menu_lst(tmpfile)
+            tmpfile.close()
+        except IOError as err:
+            raise BootmgmtConfigWriteError("Couldn't create a temporary "
+                  'file for menu.lst', err)
+
+        return [(BootConfig.OUTPUT_TYPE_FILE,
+                tmpfile.name,
+                None,
+                LegacyGRUBBootLoader.MENU_LST_PATH,
+                'root',
+                'root',
+                0644)]
+
+
+    def _write_config_odd(self, basepath):
+
+        if basepath is None:
+            raise BootmgmtInterfaceCodingError('basepath must not be None for '
+                                               'ODDBootConfig boot configs')
+
+        odd_root = self._boot_config.get_root()
+        tuples = self._write_config_generic(basepath, odd_root)
+
+        # Now add the stage2_eltorito file to the tuples list:
+        try:
+            tmpfile = tempfile.NamedTemporaryFile(dir=basepath, delete=False)
+            tmpfile.close()
+            shutil.copy(odd_root + '/boot/grub/stage2_eltorito',
+                        tmpfile.name)
+        except IOError as err:
+            raise BootmgmtConfigWriteError('Error while trying to copy '
+                  'Legacy GRUB El Torito stage2', err)
+
+        tuples += [(BootConfig.OUTPUT_TYPE_BIOS_ELTORITO,
+                    tmpfile.name,
+                    None,
+                    None,
+                    None,
+                    None,
+                    None)]
+
+        return tuples
+
+    # Generic support methods for all BootConfig classes
+
+    def _write_menu_lst(self, outfile):
+        propdict = LegacyGRUBBootLoader.DEFAULT_PROPDICT.copy()
+
+        minmem64 = self._bl_props.get(BootLoader.PROP_MINMEM64, None)
+        if not minmem64 is None:
+            propdict['minmem64'] = 'min_mem64 ' + str(minmem64)
+
+        timeout = self._bl_props.get(BootLoader.PROP_TIMEOUT, None)
+        if timeout is None:
+            timeout = LegacyGRUBBootLoader.DEFAULT_TIMEOUT
+        propdict['timeout_command'] = 'timeout ' + str(timeout)
+
+        hidemenu = self._bl_props.get(BootLoader.PROP_QUIET, None)
+        if hidemenu is True:
+            propdict['hidemenu'] = 'hiddenmenu'
+
+        consprop = self._bl_props.get(BootLoader.PROP_CONSOLE, None)
+        if consprop is None or consprop == BootLoader.PROP_CONSOLE_GFX:
+            propdict['splashimage_command'] = (
+               'splashimage /boot/grub/splash.xpm.gz')
+            propdict['foreground'] = (
+               '\nforeground ' + LegacyGRUBBootLoader.DEFAULT_FORECOLOR)
+            propdict['background'] = (
+               '\nbackground ' + LegacyGRUBBootLoader.DEFAULT_BACKCOLOR)
+        elif consprop == BootLoader.PROP_CONSOLE_SERIAL:
+            params = self._serial_parameters()
+
+            sercmd = 'serial --unit=%s' % params[BootLoader.PROP_SP_PORT]
+
+            if not params[BootLoader.PROP_SP_SPEED] is None:
+                sercmd += ' --speed=%s' % params[BootLoader.PROP_SP_SPEED]
+            if not params[BootLoader.PROP_SP_DATAB] is None:
+                sercmd += ' --word=%s' % params[BootLoader.PROP_SP_DATAB]
+            if not params[BootLoader.PROP_SP_PARITY] is None:
+                sercmd += ' --parity=%s' % params[BootLoader.PROP_SP_PARITY]
+            if not params[BootLoader.PROP_SP_STOPB] is None:
+                sercmd += ' --stop=%s' % params[BootLoader.PROP_SP_STOPB]
+
+            propdict['serial_command'] = sercmd
+            propdict['terminal_command'] = 'terminal serial'
+
+        # iterate through the list of boot instances in the BootConfig
+        # instance, adding an entry for each one:
+        entries = ''
+        for idx, inst in enumerate(self._boot_config.boot_instances):
+            # XXX - Do validation of kernel against kernel under
+            # self.rootpath (if specified)
+            # XXX - Also: should we verify that a 64-bit kernel is being
+            # used with a 64-bit boot archive and similarly for 32-bit?
+            # XXX - Use the signature attribute
+            if inst.default is True:
+                propdict['default_command'] = 'default ' + str(idx)
+            entries += 'title ' + inst.title + '\n'
+            entries += self._generate_entry(inst)
+            entries += '\n'
+
+        outfile.write(LegacyGRUBBootLoader.MENU_LST_PREAMBLE % propdict)
+        outfile.write(entries)
+
+    # Menu-entry generator infrastructure
+
+    def _generate_entry(self, instance):
+        """Use the BootInstance's class name to find the entry-generator method.
+        Entry generator functions are responsible for producing a string
+        with the rest of the entry (the title is printed by the caller)"""
+
+        instclsname = instance.__class__.__name__
+        method_name = '_generate_entry_' + instclsname
+        entry_generator = self.__class__.__dict__.get(method_name, None)
+        if not entry_generator is None:
+            # It's a method, so self must be passed explicitly
+            return entry_generator(self, instance)
+        else:
+            self._debug('No entry generator (%s) for class %s' %
+                        (method_name, instclsname))
+            return ''
+
+    def _generate_entry_generic(self, inst, kargs):
+
+        ostr = ''
+        kargs = '' if kargs is None else kargs
+
+        try:
+            inst.kernel = inst.kernel % {'karch' : "$ISADIR"}
+        except KeyError:
+            # If somehow another Python conversion specifier snuck in,
+            # raise an exception
+            raise BootmgmtMalformedPropertyValueError('kernel', inst.kernel)
+
+        if not inst.kernel.find("$ISADIR") is -1:
+            ostr += 'kernel$ ' + inst.kernel
+        else:
+            ostr += 'kernel ' + inst.kernel
+
+        # kargs already has a leading space from the initialization, above
+        ostr += (' ' if not kargs == '' else '') + kargs + '\n'
+
+        try:
+            inst.boot_archive = inst.boot_archive % {'karch' : "$ISADIR"}
+        except KeyError:
+            # If somehow another Python conversion specifier snuck in,
+            # raise an exception
+            raise BootmgmtMalformedPropertyValueError('boot_archive',
+                                                      inst.boot_archive)
+
+        if not inst.boot_archive.find("$ISADIR") is -1:
+            ostr += 'module$ ' + inst.boot_archive + '\n'
+        else:
+            ostr += 'module ' + inst.boot_archive + '\n'
+
+        return ostr
+
+
+    def _generate_entry_SolarisDiskBootInstance(self, inst):
+        "Menu-entry generator function for SolarisDiskBootInstance instances"
+
+        ostr = ''
+        kargs = ''
+        if inst.fstype == 'zfs':
+            if inst.bootfs is None:
+                raise BootmgmtIncompleteBootConfigError('bootfs property '
+                      'is missing')
+            ostr += 'bootfs ' + inst.bootfs + '\n'
+
+            if inst.kargs is None:
+                kargs = '-B $ZFS-BOOTFS'
+            elif inst.kargs.find('$ZFS-BOOTFS') is -1:
+                # XXX - This is very simplistic and should be revisited
+                kargs = '-B $ZFS-BOOTFS ' + inst.kargs
+            else:
+                kargs = inst.kargs
+        elif not inst.kargs is None and inst.kargs.strip() != '':
+            kargs = inst.kargs
+
+        ostr += self._generate_entry_generic(inst, kargs)
+
+        return ostr
+
+    def _generate_entry_SolarisODDBootInstance(self, inst):
+        return self._generate_entry_generic(inst, inst.kargs)
+
+    def _generate_entry_ChainDiskBootInstance(self, inst):
+        """Menu-entry generator for ChainDiskBootInstance instances.  This is a
+        VERY simple use of the rootnoverify and chainloader Legacy GRUB
+        commands.  Chainloading is only supported to a specific, numbered
+        drive and physical sector offset/count."""
+
+        if inst.chaininfo is None:
+            raise BootmgmtIncompleteBootConfigError('chaininfo property is '
+                  'missing')
+        if not type(inst.chaininfo) is tuple or len(inst.chaininfo) == 0:
+            raise BootmgmtArgumentError('chaininfo must be a non-zero-length '
+                                        'tuple')
+        if not type(inst.chaininfo[0]) is int:
+            raise BootmgmtArgumentError('chaininfo[0] must be an int')
+        if len(inst.chaininfo) > 1 and not type(inst.chaininfo[1]) is int:
+            raise BootmgmtArgumentError('chaininfo[1] must be an int')
+
+        diskstr = '(hd' + str(inst.chaininfo[0])
+
+        if len(inst.chaininfo) > 1:
+            diskstr += ',' + str(inst.chaininfo[1])
+
+        diskstr += ')'
+
+        ostr = 'rootnoverify ' + diskstr + '\n'
+        ostr += 'chainloader '
+        if int(inst.chainstart) != 0:
+            ostr += str(int(inst.chainstart))
+        ostr += '+' + str(int(inst.chaincount)) + '\n'
+        return ostr
+
+    def _prop_validate(self, key, value=None, validate_value=False):
+        if key == BootLoader.PROP_BOOT_TARGS:
+            if validate_value is True and value != 'bios':
+                raise BootmgmtUnsupportedPlatformError(value + ' is not a '
+                      'supported firmware type for ' + self.__class__.__name__)
+            return
+        super(self.__class__, self)._prop_validate(key, value, validate_value)
+
+    # Property-related methods
+
+    def setprop(self, key, value):
+        """Set a Boot Loader property.  Raises BootmgmtUnsupportedPropertyError
+        if the property is not supported"""
+
+        self._prop_validate(key, value, True)
+
+        # XXX - Check that the value for each property is well-formed
+        if self._bl_props.get(key, None) != value:
+            self._bl_props[key] = value
+            self.dirty = True
+
+    def getprop(self, key):
+        "Get a Boot Loader property"
+
+        self._prop_validate(key)
+
+        return self._bl_props.get(key, None)
+
+    def delprop(self, key):
+        "Delete a Boot Loader property"
+
+        self._prop_validate(key)
+
+        # Check pseudoproperties:
+        if key == BootLoader.PROP_BOOT_TARGS:
+            raise BootmgmtUnsupportedOperationError("key `%s' may not be "
+                                                    "deleted" % key)
+        try:
+            del self._bl_props[key]
+            self.dirty = True
+        except KeyError as err:
+            raise BootmgmtUnsupportedOperationError("key `%s' does not exist"
+                   % key, err)
+
+    # BootLoader installation methods
+
+    def install(self, location):
+
+        if isinstance(location, basestring):
+            try:
+                filemode = os.stat(location).st_mode
+            except OSError as err:
+                raise BootLoaderInstallError('Error stat()ing %s' % location,
+                                             err)
+
+            if stat.S_ISDIR(filemode):
+                # We have been given an output directory.  Produce the menu.lst
+                # there.
+                return self._write_config(location)
+
+                # XXX - Handle other types of boot_class here (copying pxegrub,
+                # stage2_eltorito)
+            elif stat.S_ISCHR(filemode):
+                self._write_loader(location)
+                # Now write the menu.lst:
+                self._write_config(None)
+            else:
+                raise BootmgmtArgumentError('Invalid location argument (%s)'
+                                            % location)
+        else:
+            for devname in location:
+                try:
+                    filemode = os.stat(devname).st_mode
+                except OSError as err:
+                    self._debug('Error stat()ing %s' % devname)
+                    raise BootLoaderInstallError('Error stat()ing %s' % devname,
+                                                 err)
+                if stat.S_ISCHR(filemode):
+                    self._write_loader(devname)
+                else:
+                    raise BootmgmtArgumentError('%s is not a characters-special'
+                                                ' file' % devname)
+
+            self._write_config(None)
+
+        return None
+
+    # _write_loader performs the real guts of boot loader installation
+    def _write_loader(self, devname):
+        """Invoke installgrub to write stage1 and stage2 to disk.  If 
+        devname is a p0 node,  pass -m to installgrub"""
+
+        # Transform the devname if s0 wasn't passed in
+        mbr = False
+        realdev = devname
+
+        if len(devname) > 2 and (devname[-2] == 'p' and devname[-1].isdigit()):
+            mbr = (devname[-1] == '0' or int(devname[-1]) > 4)
+            realdev = devname[:-2] + 's0'
+        elif len(devname) > 3 and (devname[-3] == 'p' and
+             devname[-2].isdigit() and devname[-1].isdigit()):
+            realdev = devname[:-3] + 's0'
+            mbr = True  # Extended partition specified, MBR is required
+
+        args = ['/usr/sbin/installgrub']
+
+        if mbr is True:
+            args += ['-m']
+
+        # If a version is present, try to use it during installation.
+        if not self.version is None:
+            args += ['-u', self.version]
+
+        args += [self.rootpath + '/boot/grub/stage1',
+                 self.rootpath + '/boot/grub/stage2',
+                 realdev]
+
+        self._debug('_write_loader: Invoking command: ' + ' '.join(args))
+
+        try:
+            Popen(args, stdout=Popen.PIPE, stderr=Popen.PIPE)
+        except CalledProcessError as cpe:
+            self._debug('_write_loader: Return code = %d' % cpe.returncode)
+            if cpe.returncode != LegacyGRUBBootLoader.INSTALLGRUB_NOUPDT:
+                output = ''
+                if not cpe.popen is None and not cpe.popen.stderr is None:
+                    output = '\nOutput was:\n' + cpe.popen.stderr
+                raise BootLoaderInstallError('installgrub failed for '
+                      'device ' + devname + ': Return code ' +
+                      str(cpe.returncode) + output)
+
+    def _get_loader_version(self, devname):
+        """[DEPRECATED AND CURRENTLY UNUSED] Invoke installgrub -ie and
+        return the first line (version string)."""
+
+        args = ['/usr/sbin/installgrub', '-ie', devname]
+        try:
+            proc = Popen(args, stdout=Popen.STORE, stderr=Popen.STORE)
+            version_string = proc.stdout.split('\n')[0]
+            return version_string
+        except CalledProcessError as cpe:
+            if cpe.returncode != LegacyGRUBBootLoader.INSTALLGRUB_NOEINFO:
+                output = ''
+                if not cpe.popen is None and not cpe.popen.stderr is None:
+                    output = '\nOutput was:\n' + cpe.popen.stderr
+                self._debug('installgrub version check returned ' +
+                            str(cpe.returncode) + '.  Ignoring.' + output)
+            return None
+
+#
+# Legacy GRUB menu.lst
+#
+class LegacyGRUBMenuFile(MenuDotLst):
+    def __init__(self, filename='/boot/solaris/menu.lst'):
+        super(LegacyGRUBMenuFile, self).__init__(filename)
+
+    def _analyze_syntax(self):
+        """
+        Command      := Keyword Arguments | VarName '=' Value
+        Arguments    := Arguments [ \t]+ Argument | Argument
+        Keyword      := 'blocklist' | 'boot' | 'bootfs' | 'bootp' |
+                        'cat' | 'chainloader' | 'cmp' | 'color' |
+                        'configfile' | 'debug' | 'default' |
+                        'device' | 'dhcp' | 'displayapm' |
+                        'displaymem' | 'embed' | 'fallback' |
+                        'find' | 'findroot' | 'fstest' | 'geometry' |
+                        'halt' | 'help' | 'hiddenmenu' | 'hide' |
+                        'ifconfig' | 'impsprobe' | 'initrd' |
+                        'install' | 'ioprobe' | 'kernel$' |
+                        'kernel' | 'lock' | 'makeactive' | 'map' |
+                        'md5crypt' | 'min_mem64' | 'module$' |
+                        'module' | 'modulenounzip' | 'pager' |
+                        'partnew' | 'parttype' | 'password' |
+                        'pause' | 'quit' | 'rarp' | 'read' |
+                        'reboot' | 'root' | 'rootnoverify' |
+                        'savedefault' | 'serial' | 'setkey' |
+                        'setup' | 'terminal' | 'terminfo' |
+                        'testload' | 'testvbe' | 'tftpserver' |
+                        'timeout' | 'title' | 'unhide' |
+                        'uppermem' | 'vbeprobe'
+        Argument     := [^ \t\n]+
+        VarName      := [A-Za-z0-9]+
+        Value        := [^\n]+
+        """
+        # XXX - Currently a NOP
+
+
+def bootloader_classes():   
+    return [LegacyGRUBBootLoader]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/menulst.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,259 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+menu.lst parser implementation for pybootmgmt
+"""
+
+import re
+
+class MenuLstError(Exception):
+    def __init__(self, msg):
+        super(LegacyGRUBMenuError, self).__init__()
+        self.msg = msg
+
+    def __str__(self):
+        return self.msg
+
+
+class MenuLstCommand(object):
+    """A menu.lst command and its arguments from the menu.lst file"""
+
+    def __init__(self, command, args=None):
+        self._command = command
+        self._args = list(args) if not args is None else []
+
+    def get_command(self):
+        return self._command
+
+    def get_args(self):
+        return self._args
+
+    def __str__(self):
+        if not self._command is None and not self._args is None:
+            return self._command + ' ' + ' '.join(self._args)
+        elif not self._command is None:
+            return self._command
+        else:
+            return ''
+
+    def __repr__(self):
+        ostr = ('(' + repr(self._command) + ',' +
+               repr(self._args) + ')')
+        return ostr
+
+class MenuLstMenuEntry(object):
+    """Representation of a menu.lst menu entry, which consists of a list of
+    MenuLstCommand objects (the first of which must be the 'title' command).
+
+    <instance>._cmdlist = [MenuLstCommand #1, MenuLstCommand #2, ...]
+    """
+
+    def __init__(self, args=None):
+        if args is None:
+            self._cmdlist = []
+        else:
+            self._cmdlist = list(args) # make a copy
+
+    def add_command(self, mlcmd):
+        self._cmdlist.append(mlcmd)
+
+    def add_non_command(self, noncmd):
+        "Add a string (blank line or comment) to the command list"
+        self._cmdlist.append(noncmd)
+
+    def find_command(self, name):
+        for cmd in self._cmdlist:
+            if isinstance(cmd, MenuLstCommand) and name == cmd.get_command():
+                return True
+        return False
+
+    def commands(self):
+        return self._cmdlist
+
+    def __str__(self):
+        ostr = 'MenuLstMenuEntry {\n'
+        for cmd in self._cmdlist:
+            ostr += '\t' + str(cmd).rstrip('\n') + '\n'
+        ostr += '}'
+        return ostr
+
+    def __repr__(self):
+        ostr = '['
+        i = 0
+        if len(self._cmdlist) >= 1:
+            for i in range(len(self._cmdlist) - 1):
+                ostr += repr(self._cmdlist[i]) + ', '
+        if len(self._cmdlist) >= 2:
+            ostr += repr(self._cmdlist[i + 1])
+        ostr += ']'
+        return ostr
+
+
+class MenuDotLst(object):
+
+    def __init__(self, filename):
+        self.target = self
+        self._line = 0
+        self._last = ''
+        self._entitylist = []        # per-instance list of entities
+        self._filename = filename
+        self._parse()
+
+    def entities(self):
+        "Return a list of entities encapsulated by this MenuDotLst"
+        return self._entitylist
+
+    def add_command(self, cmd):
+        "Add a MenuLstCommand to the entitylist"
+        self._entitylist.append(cmd)
+
+    def __str__(self):
+        ostr = ''
+        for entity in self._entitylist:
+            ostr += str(entity) + '\n'
+        return ostr
+
+    def __repr__(self):
+        return (repr(self._entitylist))
+
+    def _parse(self):
+        "Parse the menu.lst file"
+        fileobj = open(self._filename)
+        try:
+            for line in fileobj:
+                self._parse_line(line)
+            self._parse_line(None)    # end of file reached
+        finally:
+            fileobj.close()
+
+        self._analyze_syntax()
+
+    def _analyze_syntax(self):
+        "This can be overridden in child classes, if needed"
+        pass
+
+    @staticmethod
+    def _process_escapes(istr):
+        res = ''
+        newline_escaped = False
+        for idx in range(len(istr)):
+            #
+            # Legacy GRUB allows escaping the newline
+            #
+            # We're guaranteed to always have a character
+            # after the backslash, so no try is needed here.
+            if istr[idx] == '\\' and istr[idx + 1] == '\n':
+                newline_escaped = True
+                res += ' '
+            elif istr[idx] != '\n':
+                res += istr[idx]
+        return res, newline_escaped
+
+    def _parse_line(self, nextline):
+        """Parses a line of a menu.lst configuration file.
+        The grammar of the menu.lst configuration file is simple:
+        Comments are lines that begin with '#' (with or without
+        preceeding whitespace), and commands are non-comments that
+        include one or more non-whitespace character sequences,
+        separated by whitespace.  The commands are not checked
+        for semantic correctness.  They're just stored for later
+        analysis.
+
+        The parser works as follows:
+
+        If the line is None, parsing is complete, so save the last
+        entry processed to the statement list.
+
+        If the line (stripped of any leading whitespace) starts with
+        '#', then it's a comment, so save it and return.
+
+        Split the line into a command portion and an optional
+        argument(s) portion, taking care to process escape sequences
+        (including the special escape of the newline)
+
+        If the line begins with the 'title' keyword, then a new entry
+        is created and saved so that future commands can be added to
+        it.
+
+        If an entry is active (if we're parsing after a title command),
+        add the current command and arguments to the current entry.
+
+        If an entry is not active, add the command and arguments to
+        the statement list.
+
+        The configuration file entity list contains commands and
+        entries::
+
+        [(MenuLstCommand|CommentString)*, (MenuLstMenuEntry|CommentString)*]
+
+        Comments and blank space is stored as a plain string.
+        """
+
+        if nextline is None:
+            # If there's still text in the last-line buffer,
+            # we must have had a dangling backslash
+            if self._last != '':
+                raise MenuLstError('Dangling backslash detected')
+            return
+
+        self._line += 1
+
+        # Remove the comment portion of the line
+        stripped = nextline.strip()
+        if stripped == '' or stripped[0] == '#':
+            self.target.add_non_command(nextline)
+            return
+
+        # Remove escape sequences from the line:
+        try:
+            nextline, newline_escaped = (
+                self._process_escapes(nextline))
+        except IndexError:
+            # The error must have been a dangling backslash
+            raise MenuLstError('Dangling backslash detected')
+
+        # If the newline was escaped, save the string for later
+        if newline_escaped:
+            self._last += nextline
+            return        # Wait for the next line
+        else:
+            nextline = self._last + nextline
+            self._last = ''
+
+        pattern = (r'([^ \t"]+"([^\\"]|\\.)*"[^ \t]*|'
+              r'([^ \t\\"]|\\.)*"[^"]*(?![^"]*")|'
+              # r'"([^ \t\\"]|\\.)*(?![^"]*[^\\]")|'
+               r'[^ "\t]+)')
+        argv = [v[0] for v in re.compile(pattern).findall(nextline)]
+
+        if (len(argv) > 0):
+            if argv[0] == 'title':
+                self.target = MenuLstMenuEntry()
+                self._entitylist.append(self.target)
+            # Check argv[0] (if it exists) for an equals sign, since 
+            # that's a valid way to set certain variables
+            argv = argv[0].split('=', 1) + argv[1:]
+
+            self.target.add_command(MenuLstCommand(argv[0], argv[1:]))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/backend/loader/sbb.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,56 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Loader backend for the SPARC Boot Block (SBB)
+"""
+
+from ...bootloader import BootLoader
+
+class OBPBootLoader(BootLoader):
+    pass
+
+class WanbootBootLoader(OBPBootLoader):
+    @staticmethod
+    def probe(**kwargs):
+        return (None, None)
+
+class HSFSBootLoader(OBPBootLoader):
+    @staticmethod
+    def probe(**kwargs):
+        return (None, None)
+
+class UFSBootLoader(OBPBootLoader):
+    @staticmethod
+    def probe(**kwargs):
+        return (None, None)
+
+class ZFSBootLoader(OBPBootLoader):
+    @staticmethod
+    def probe(**kwargs):
+        return (None, None)
+
+def bootloader_classes():   
+    return [WanbootBootLoader, HSFSBootLoader, UFSBootLoader, ZFSBootLoader]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootarchive.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,33 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+* Creation and manipulation of the Solaris boot archive
+
+* Manipulation of the Solaris Boot Archive cache, designed to speed up
+archive maintainence operations by caching the contents of the boot
+archive (which consists of compressed objects on x86), allowing faster
+update of an archive when changed objects are detected
+"""
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootconfig.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,797 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Abstractions for boot configuration management.  A BootConfig object aggregates
+the boot configuration for a system.  Implementations are provided for active
+systems (systems that boot from disk devices) as well as for installation image
+creation (i.e. for network-based installation images, optical disc installation
+images, and USB-based installation images).
+BootConfig provides access, through abstractions, to the system boot loader,
+general system boot configuration variables, and the set of bootable instances
+that the boot loader provides a means to boot.
+"""
+
+import collections
+import gettext
+import unittest
+
+from . import BootmgmtNotSupportedError, BootmgmtArgumentError
+from . import BootmgmtMissingInfoError
+from . import bootinfo, bootloader
+from .bootutil import LoggerMixin, get_current_arch_string
+
+_ = gettext.translation("SUNW_OST_OSCMD", "/usr/lib/locale",
+    fallback=True).gettext
+
+class BootConfig(LoggerMixin):
+    """Abstract base class for boot configuration classes"""
+
+    # Valid flags to pass to __init__:
+    (BCF_CREATE,
+     BCF_ONESHOT,
+     BCF_AUTOGEN,
+     BCF_MIGRATE) = range(4)
+
+    BOOT_CLASS_DISK = 'disk'
+    BOOT_CLASS_NET = 'net'
+    BOOT_CLASS_ODD = 'odd'
+
+    TOKEN_SYSTEMROOT = 'systemroot'
+
+    OUTPUT_TYPE_FILE = 'file'
+    OUTPUT_TYPE_BIOS_ELTORITO = 'bios-eltorito-image'
+
+    # Tuple indices for each tuple in the list output by the install() method
+    (IDX_FILETYPE,
+     IDX_TEMPNAME,
+     IDX_INSTANCE,
+     IDX_DESTNAME,
+     IDX_USER,
+     IDX_GROUP,
+     IDX_MODE) = range(7)
+
+    @property
+    def dirty(self):
+        return self._dirty
+
+    @dirty.setter
+    def dirty(self, val):
+        if not self._dirty and val is True:
+            self._debug('dirty set')
+            self._dirty = True
+        else:
+            self._dirty = val
+
+    def __init__(self, flags, **kwargs):
+        """
+        Initialize the basic set of attributes for all BootConfig classes
+        -----------------------------------------------------------------
+        Argument|  Valid Value(s)
+        --------+--------------------------------------------------------
+        flags   | <Tuple of flags that modify the behavior of this
+                |  BootConfig object> [tuple]
+                |
+                | Allowed values in the tuple:
+                | ----------------------------
+                | BootConfig.BCF_CREATE: Create a new boot
+                |                        configuration.  If one already 
+                |                        exists, it will be discarded.
+                |                        If this flag is not present and
+                |                        no boot configuration exists,
+                |                        an exception
+                |                        (BootmgmtConfigurationReadError)
+                |                        will be raised.
+                | BootConfig.BCF_ONESHOT:If True, only the final boot
+                |                        configuration files will be
+                |                        created when the
+                |                        commit_boot_config() is called.
+                |                        This is useful for one-shot
+                |                        creation of boot configurations
+                |                        for installation media.
+                | BootConfig.BCF_AUTOGEN:The set of boot instances will be
+                |                        automatically generated by
+                |                        scanning the system.  The list of
+                |                        boot instances found by the scan
+                |                        will be available after
+                |                        the BootConfig object is created.
+                |                        This set of boot instances can
+                |                        then be tailored before calling
+                |                        commit_boot_config().  Note that
+                |                        the set of BootInstance objects
+                |                        added to this object's boot_instances
+                |                        are only guaranteed to include
+                |                        Solaris boot instances.
+                | BootConfig.BCF_MIGRATE:If supported, the existing boot
+                |                        configuration will be migrated
+                |                        from an older form to a newer
+                |                        form (i.e. conversion from legacy
+                |                        GRUB's menu.lst to GRUB2's
+                |                        configuration format). The conversion
+                |                        is performed at commit_boot_config()
+                |                        time.  Cannot be used with the
+                |                        BCF_CREATE flag.
+                |
+        platform| [optional] The target system architecture / firmware for
+        (keyword| which this boot configuration will be written.  Useful
+         arg)   | for the creation of boot configuration files for network
+                | boot instances, or when a boot configuration is created
+                | on a system of a different architecture / firmware than
+                | the target.  If this is not supplied, the assumption is
+                | that the boot configuration will be for the current
+                | system.  Supported tuple values are:
+                | { ('sparc', 'obp' (or None)),
+                |   ('x86', 'bios' (or None)),
+                |   ('x86', 'uefi64') }
+                | If a particular BootConfig subclass does not support  
+                | manipulation of a different platform's boot
+                | configuration, a BootmgmtNotSupportedError will be
+                | raised.
+        -----------------------------------------------------------------
+        """
+
+        self.boot_instances = []
+        self.boot_class = kwargs.get('boot_class', None)
+        self.boot_fstype = kwargs.get('boot_fstype', None)
+        self._platform = kwargs.get('platform', None)
+        self._dirty = False
+        self._flags = flags
+        self.boot_loader = self._get_boot_loader(**kwargs)
+
+        if (BootConfig.BCF_CREATE in self._flags and
+            BootConfig.BCF_MIGRATE in self._flags):
+            raise BootmgmtUnsupportedOperationError(
+                  'Migration cannot be combined with creation')
+
+        if BootConfig.BCF_CREATE in self._flags:
+            self._new_boot_config(**kwargs)
+        else:
+            # _load_boot_config raises BootmgmtConfigurationReadError
+            self._load_boot_config(**kwargs)
+
+        if BootConfig.BCF_AUTOGEN in self._flags:
+            self._autogenerate_config(**kwargs)
+
+    def _autogenerate_config(self, **kwargs):
+        from .backend.autogen import BootInstanceAutogenFactory
+        inst_list = BootInstanceAutogenFactory.autogen(self)
+        if len(inst_list) > 0:
+            self.boot_instances += inst_list
+            self.dirty = True
+
+    def _load_boot_config(self, **kwargs):
+        """[Implemented by child classes] Loads the boot configuration (with
+        guidance from kwargs)."""
+        pass
+
+    def _new_boot_config(self, **kwargs):
+        """[Implemented by child classes] Initializes this instance with
+        a new boot configuration (with guidance from kwargs)."""
+        pass
+
+    def _get_boot_loader(self, **kwargs):
+        """Initializes this instance's boot_loader"""
+        loader = bootloader.BootLoader.get(bootconfig=self)
+        self._debug('loader = ' + str(loader))
+        return loader
+
+    def get_root(self):
+        "Return the root directory where this BootConfig is be stored"
+        return None
+
+    def add_boot_instance(self, boot_instances, where=-1):
+        """
+        Adds one or more new boot instances to this boot configuration.
+        boot_instance is a list of BootInstance references or a single
+        BootInstance reference.
+        The where argument determines where in the ordered list of boot
+        instances this boot instance entry will be placed.  The default
+        is -1, which means the end of the list of boot instances.  If
+        where is a callable object, it will be used to iterate through
+        the boot instances list.  If it returns a negative value, the
+        boot_instance will be inserted BEFORE that item;  If it returns
+        a positive value, the boot_instance will be inserted AFTER that
+        item;  If it returns 0, no insertion will occur at that point
+        in the iteration.  The arguments to the function are
+        (<current_boot_instance_in_the_iteration>, boot_instance).
+        """
+
+        if isinstance(boot_instances, collections.Iterable):
+            for instance in boot_instances:
+                self._add_one_boot_instance(instance, where)
+                # If where is not -1, make sure we increment it so that
+                # subsequent boot instances are placed in the boot_instances
+                # array in the proper order
+                if where > -1:
+                    where = where + 1
+        else:
+            self._add_one_boot_instance(boot_instances, where)
+
+
+    def _add_one_boot_instance(self, boot_instance, where=-1):
+
+        prevDefault = None
+
+        if boot_instance.default is True:
+            # Find previous default (if any)
+            defaults = [x for x in self.boot_instances
+                        if x.default is True]
+            if len(defaults) > 0:
+                # XXX - assert here that len(defaults) is 1?
+                prevDefault = defaults[0]
+                self._debug("Previous default was:\n%s" % str(prevDefault))
+
+        if type(where) is int:
+            # where is an index if it's > 0
+            where = where if where >= 0 else len(self.boot_instances)
+            self.boot_instances[where:where] = [boot_instance]
+            boot_instance._bootconfig = self
+            self.dirty = True
+        elif callable(where):
+            for index, inst in enumerate(self.boot_instances):
+                whereval = where(inst, boot_instance)
+                if whereval < 0:
+                    self.boot_instances[index:index] = [boot_instance]
+                    boot_instance._bootconfig = self
+                    self.dirty = True
+                    break
+                elif whereval > 0:
+                    self.boot_instances[index + 1: index + 1] = [boot_instance]
+                    boot_instance._bootconfig = self
+                    self.dirty = True
+                    break
+        else:
+            raise BootmgmtArgumentError('The where parameter is malformed')
+
+        if not prevDefault is None:
+            prevDefault.default = False
+
+    def delete_boot_instance(self, filter_func, all=True):
+        """
+        Deletes one or more boot instances from the boot configuration.
+        filter_func is a function that takes a single argument (a
+        BootInstance).  If it returns True, that BootInstance is removed
+        from the boot configuration.
+        The 'all' parameter is True if all matching boot instances are
+        to be deleted.  If False, only the first instance for which
+        filter_func returns True is deleted.
+           For example:
+              delete_boot_instance(lambda x: x.title() == 'snv_158')
+           will delete all boot instances whose titles match 'snv_158'.
+        """
+        if all is True:
+            oldlen = len(self.boot_instances)
+            self.boot_instances = filter(lambda x: not filter_func(x),
+                                         self.boot_instances)
+            if oldlen != len(self.boot_instances):
+                self.dirty = True
+        else:
+            for idx, inst in enumerate(self.boot_instances):
+                if filter_func(inst) is True:
+                    del self.boot_instances[idx]
+                    self.dirty = True
+                    return
+
+    def modify_boot_instance(self, filter_func, mod_func):
+        """
+        Applies mod_func to all boot instances for which filter_func
+        returns True.
+        This is shorthand for:
+
+          for bi in filter(filter_func, bootconfig_obj.boot_instances):
+              mod_func(bi)
+
+        (The (single) argument to filter_func and mod_func is a
+        BootInstance ref).
+        """
+        for inst in filter(filter_func, self.boot_instances):
+            mod_func(inst)
+
+    def commit_boot_config(self, temp_dir=None, boot_devices=None):
+        """Writes the boot configuration (including boot instances and boot
+        loader settings) to stable storage.
+
+        If this object was created with the BootConfig.BC_ONESHOT flag,
+        then only the final boot configuration file(s) will be created
+        (i.e. the Legacy GRUB menu.lst, GRUB2 configuration file, or
+        SPARC menu.lst)-- any other state files that store boot
+        configuration information will not be written (customizations to
+        this boot configuration may not persist).  This may prevent
+        incremental modifications to the boot configuration from
+        being possible (depending on the boot loader in use).  BC_ONESHOT
+        should only be used when creating a boot configuration that is
+        not intended to change (i.e. when creating a boot configuration
+        for use with an install image, not a running system).
+
+        boot_devices is a list of strings, each of which is the path
+        to a character-special device where the boot loader should be
+        installed.  This argument should be omitted when the consumer
+        desires to write the boot configuration files to a temporary
+        directory.
+
+        If temp_dir is not None, then the set of files that constitute
+        the boot configuration is written to a temporary directory (and
+        not to the official location(s)).  (temp_dir must exist or an  
+        IOError will be raised).  When files are written to temp_dir,   
+        commit_boot_config() returns a list of 7-tuples of the following
+        form:
+         (<type>, <srcpath>, <object>, <destpath>, <uid>, <gid>, <mode>)
+        Each of these tuples describes a boot configuration file that
+        was written, along with the required system-relative path where
+        it must be copied.  This enables a consumer to install the
+        file(s) into the correct place(s) on a system or install image
+        without having to hard-code knowledge of the filenames)).
+        For example:
+        [('file', '/tmp/bc.Jzx1cZa/boot/solaris/bootenv.rc',
+          <bootmgmt.bootconfig.SolarisDiskBootInstance object at ...>,
+          '%(systemroot)s/boot/solaris/bootenv.rc', 'root', 'sys', 0644)]
+
+        The <type> value in the tuple identifies the type of file in
+        the tuple.  This is useful for conveying platform-specific
+        attributes of a particular file.  For example, <type> could be
+        'eltorito' to identify an eltorito boot image, which a consumer
+        would recognize and then use to supply the argument to
+        `mkisofs -b`.  See child class definitions for additional <type>
+        values.  Only <type>='file' is defined at the BootConfig level.
+
+        The <object> element, if not None, is a reference to a
+        BootInstance object.  It provides clarification for tokens that
+        are embedded in the destination path.  In the above example, the
+        `systemroot' token refers to the root path of the Solaris BE
+        identified by the SolarisDiskBootInstance object in the tuple.
+        In this example, including the object allows the consumer to
+        resolve the root path location.
+
+        Note the use of Python string-formatting tokens, for which a
+        consumer must supply definitions.  The following list of tokens
+        are defined at the BootConfig level; child classes may define
+        additional tokens:
+        ----------------------------------------------------------------
+        Token Name        |  Meaning
+        ------------------+---------------------------------------------
+        systemroot        | The path to a boot instance's mounted root
+                          | filesystem. [string]
+        ----------------------------------------------------------------
+        """
+        if temp_dir is None:
+            if self.dirty is True:
+                for inst in self.boot_instances:
+                    if (not inst.boot_vars is None and
+                       inst.boot_vars.dirty is True):
+                        inst.boot_vars.write(inst=inst)
+
+            if self.dirty is True or self.boot_loader.dirty is True:
+                self.boot_loader.install(boot_devices)
+
+            return None
+        else:
+            tuple_list = []
+            if self.dirty is True:
+                for inst in self.boot_instances:
+                    if (not inst.boot_vars is None and
+                       inst.boot_vars.dirty is True):
+                        tuple_list.append(inst.boot_vars.write(inst, temp_dir))
+
+            if self.dirty is True or self.boot_loader.dirty is True:
+                tuple_list.append(self.boot_loader.install(temp_dir))
+
+            return tuple_list
+
+
+    def __str__(self):
+        s = 'State: ' + ('dirty' if self.dirty else 'clean') + '\n'
+        s += 'Class: ' + (self.boot_class
+                          if not self.boot_class is None else 'none') + '\n'
+        s += 'FSType: ' + ((self.boot_fstype
+                           if not self.boot_fstype is None else 'unknown') +
+                          '\n')
+        s += 'Boot instances: ' + str(len(self.boot_instances)) + '\n'
+        for idx, inst in enumerate(self.boot_instances):
+            s += '===[ Instance ' + str(idx) + ' ]===\n'
+            s += '\t' + str(inst).replace('\n', '\n\t') + '\n'
+        s += '===[ End Boot Instances ]===\n'
+        if not self.boot_loader is None:
+            s += 'Boot loader:\n'
+            s += str(self.boot_loader) + '\n'
+        else:
+            s += 'No boot loader'
+        return s
+
+
+class DiskBootConfig(BootConfig):
+    """A class for managing the boot configuration stored on hard disk-like
+    storage devices.  Handles Solaris boot configurations stored in ZFS root
+    pools and on devices with a UFS root filesystem"""
+
+    # Key names for keyword arguments to the constructor
+    ARG_ZFS_RPNAME = 'rpname'         # ZFS root pool name
+    ARG_ZFS_TLDPATH = 'tldpath'       # ZFS top-level dataset mounted path
+    ARG_ZFS_SYSROOT_PATH = 'zfspath'  # ZFS system root mounted path
+    ARG_UFS_ROOT = 'ufsroot'          # UFS root mounted path
+    
+    # Tokens returned from commit_boot_config()
+    TOKEN_ZFS_RPOOL_TOP_DATASET = 'rpool_top_dataset'
+
+    def __init__(self, flags, **kwargs):
+        # DiskBootConfig does not support platforms other than native
+        platform = kwargs.get('platform', None)
+        if not platform is None:
+            raise BootmgmtNotSupportedError(self.__class__.__name__ + ' does '
+                  'not support cross-platform operations')
+
+        fstype = None
+
+        if (DiskBootConfig.ARG_ZFS_RPNAME in kwargs and
+            DiskBootConfig.ARG_ZFS_TLDPATH in kwargs and
+            DiskBootConfig.ARG_ZFS_SYSROOT_PATH in kwargs):
+            fstype = 'zfs'
+            self.sysroot = kwargs[DiskBootConfig.ARG_ZFS_SYSROOT_PATH]
+            self.zfstop = kwargs[DiskBootConfig.ARG_ZFS_TLDPATH]
+            self.zfsrp = kwargs[DiskBootConfig.ARG_ZFS_RPNAME]
+        elif DiskBootConfig.ARG_UFS_ROOT in kwargs:
+            fstype = 'ufs'
+            self.sysroot = kwargs[DiskBootConfig.ARG_UFS_ROOT]
+
+        if fstype is None:
+            raise BootmgmtNotSupportedError('The filesystem type supplied '
+                'to the DiskBootConfig constructor was not recognized')
+
+        super(self.__class__, self).__init__(flags,
+                                    boot_class=BootConfig.BOOT_CLASS_DISK,
+                                    boot_fstype=fstype, **kwargs)
+    def get_root(self):
+        return self.sysroot
+
+    def _load_boot_config(self, **kwargs):
+        """Loads the boot configuration"""
+        self.boot_loader.load_config()
+
+    def _new_boot_config(self, **kwargs):
+        """Initializes this instance with a new boot configuration"""
+        self.boot_loader.new_config()
+
+
+class ODDBootConfig(BootConfig):
+    """A class for managing the boot configuration stored on optical disk
+    storage devices.  Handles Solaris boot configurations stored on DVD
+    media"""
+
+    def __init__(self, flags, **kwargs):
+        ""
+        # Weed out unsupported flags:
+        if BootConfig.BCF_MIGRATE in flags:
+            raise BootmgmtUnsupportedOperationError(self.__class__.__name__ +
+                                                    ': Migration is not supported')
+
+        # Save the image's root directory:
+        self.odd_image_root = kwargs.get('oddimage_root', None)
+        if self.odd_image_root is None:
+            raise BootmgmtArgumentError('Missing oddimage_root argument')
+
+        super(self.__class__, self).__init__(flags,
+                                    boot_class=BootConfig.BOOT_CLASS_ODD,
+                                    **kwargs)
+
+    def get_root(self):
+        return self.odd_image_root
+
+
+
+###############################################################################
+    
+
+class BootInstance(LoggerMixin):
+    """BootInstance is the core abstraction for a bootable instance of an
+    operating system.  BootConfig objects aggregate zero or more
+    BootInstance objects.  A BootInstance should always be associated
+    with a BootConfig object (so that if a consumer modifies a
+    BootInstance, the BootConfig is notified that its state has
+    changed so it can write updated configuration information at
+    BootConfig.commit_boot_config()-time).  This association is made
+    when a BootInstance is passed to BootConfig.add_boot_instance()."""
+
+    # Valid attributes ('default' is a special case)
+    _attributes = { 'title' : None }
+
+    def __init__(self, rootpath, **kwargs):
+        # If the child class added its own set of attributes, just append to
+        # it; overwise, set it to the default set from this class
+        (self.__dict__.setdefault('_attributes', {})
+                     .update(BootInstance._attributes))
+
+        self._bootconfig = None
+        self._default = False
+        self.boot_vars = None
+        self.rootpath = None
+
+        # Only add attributes to this instance if they were not overridden
+        # by the caller's keyword arguments
+        for key, value in self._attributes.items():
+            # If the key wasn't already set in this instance (in
+            # init_from_rootpath), init it to a default value here
+            if not key in kwargs and not key in self.__dict__:
+               self._debug('DEFAULT: Setting %s="%s"' %
+                            (str(key), str(value)))
+               self.__setattr__(key, value)
+
+        # If the user passed in keyword args, add them as attributes
+        for key, value in kwargs.items():
+            # If the key wasn't already set in this instance (in
+            # init_from_rootpath), init it to a default value here
+            if key in self.__dict__:
+                continue
+            self._debug('KWARGS: Setting %s="%s"' % (str(key), str(value)))
+            self.__setattr__(key, value)
+
+        # init_from_rootpath must be called after all instance variables
+        # have been set
+        if not rootpath is None:
+            self.init_from_rootpath(rootpath)
+
+    @property
+    def default(self):
+        return self._default
+
+    def init_from_rootpath(self, rootpath):
+        """Initialize the boot instance with information from the root path
+        given."""
+        self.rootpath = rootpath
+        self.boot_vars = self._get_boot_vars(rootpath)
+
+    def _set_default(self, value):
+        """By setting this BootInstance to be the default, the previous default
+        must be disabled.  This property setter function does just that by
+        using the _bootconfig reference to find the list of boot instances,
+        locating the previous default, and setting it to False.  Note that
+        only setting the default to True triggers this search (otherwise, we
+        could end up in an infinite recursive loop)."""
+
+        if not value is False and not value is True:
+            raise BootmgmtArgumentError('default must be either True or False')
+
+        if self._default == value: # Nothing to do
+            return
+
+        if value is False:
+            self._default = False
+        else:
+            self._default = True
+
+            if not self._bootconfig is None:
+                for inst in self._bootconfig.boot_instances:
+                    if not inst is self and inst.default is True:
+                        self._debug('Found previous default -- clearing it')
+                        # The following assignment's side-effect marks the
+                        # BootConfig dirty
+                        inst.default = False
+                        return
+
+        if not self._bootconfig is None:
+            # If we got here, then either the value is False or it was True
+            # and no previous default was found, so mark the BootConfig dirty
+            self._bootconfig.dirty = True
+
+    def _get_boot_vars(self, rootpath):
+        """Initializes this instance's boot_vars"""
+        return bootinfo.BootVariables.get(sysroot=rootpath)
+
+    def __setattr__(self, key, value):
+        """Intercept the set attribute method to enforce setting of a particular
+        set of boot instance attributes (the union of those defined in child
+        classes and this class).  'default' is treated specially (see
+        _set_default())"""
+
+        if key == 'default':
+            self._debug('key="%s" value="%s"' % (key, value))
+            return self._set_default(value)
+
+        self.__dict__[key] = value
+        if key in self._attributes and not self._bootconfig is None:
+            self._debug('key="%s" value="%s"' % (key, value))
+            if not self._bootconfig is None:
+                self._bootconfig.dirty = True
+
+    def __delattr__(self, key):
+        """Delete an attribute from this BootInstance.  'default' is treated
+        specially, as with __setattr__."""
+
+        if key == 'default':
+            self._debug('delete key="default"')
+            self._set_default(False)  # Same effect as setting it to False
+            return
+
+        if not key in self.__dict__:
+            raise AttributeError(str(key))
+
+        del self.__dict__[key]
+
+        if key in self._attributes and not self._bootconfig is None:
+            self._debug('delete key="%s"' % key)
+            if not self._bootconfig is None:
+                self._bootconfig.dirty = True
+
+    def __str__(self):
+        s = ''
+        if self.default is True:    # only print default if it's True
+            s += 'default = True\n'
+        for item in self._attributes.keys():
+            s += str(item) + ' = '
+            s += str(self.__dict__.get(item, '<Not Defined>')) + '\n'
+        if not self.boot_vars is None:
+            s += '===[ %d Boot variables ]===\n' % len(self.boot_vars)
+            s += '\n'.join(map(str, self.boot_vars))
+            s += '\n===[ End Boot Variables ]==='
+        return s
+
+
+class ChainDiskBootInstance(BootInstance):
+    """A boot instance of a chainloaded operating system"""
+
+    _attributes = { 'chaininfo'  : None,
+                    'chainstart' : '0',
+                    'chaincount' : '1' }
+
+    def __init__(self, rootpath=None, **kwargs):
+        """rootpath is not supported, so should remain `None'"""
+        (self.__dict__.setdefault('_attributes', {})
+                      .update(ChainDiskBootInstance._attributes))
+        super(ChainDiskBootInstance, self).__init__(None, **kwargs)
+
+
+class SolarisBootInstance(BootInstance):
+    """Abstraction for a Solaris Boot Instance.  Supported attributes are:
+               - kernel [string] [optional]
+               - boot_archive [string] [optional]
+               - kargs [string] [optional]: Kernel argument string
+               - signature [string] [optional]: The "boot signature" of this
+                                                boot instance.
+    """
+
+    if get_current_arch_string() == 'x86':
+        _attributes = {
+                      'kernel' : '/platform/i86pc/kernel/%(karch)s/unix',
+                      'boot_archive' : '/platform/i86pc/%(karch)s/boot_archive',
+                      'kargs' : None,
+                      'signature' : None,
+                      }
+    else:
+        _attributes = {}
+
+
+    def __init__(self, rootpath, **kwargs):
+        # If the child class added its own set of attributes, just append to
+        # it; overwise, set it to the default set from this class
+        (self.__dict__.setdefault('_attributes', {})
+                      .update(SolarisBootInstance._attributes))
+
+        super(SolarisBootInstance, self).__init__(rootpath, **kwargs)        
+
+
+class SolarisDiskBootInstance(SolarisBootInstance):
+    """Abstraction for a Disk-based Solaris Boot Instance.  Additional
+       attributes supported are:
+               - fstype [string] [required]: One of: [ 'ufs', 'zfs' ]
+               - If fstype == 'zfs':
+                 * bootfs [string] [required]
+    """
+    _attributes = { 'fstype' : 'zfs',
+                    'bootfs' : None }
+
+    def __init__(self, rootpath, **kwargs):
+        # If the child class added its own set of attributes, just append to
+        # it; overwise, set it to the default set from this class
+        (self.__dict__.setdefault('_attributes', {})
+                      .update(SolarisDiskBootInstance._attributes))
+
+        super(SolarisDiskBootInstance, self).__init__(rootpath, **kwargs)
+
+        if self.fstype == 'zfs':
+            if not 'bootfs' in kwargs:
+                raise BootmgmtMissingInfoError('missing bootfs arg')
+            # Make sure bootfs appears to be well-formed
+            bootfs_spec = kwargs['bootfs'].split('/', 2)
+            if len(bootfs_spec) != 3 or bootfs_spec[1] != 'ROOT':
+                raise BootmgmtArgumentError('Invalid bootfs: %s' %
+                                            kwargs['bootfs'])
+
+        # If title is STILL None, try an alternate (the last component of the
+        # bootfs):
+        try:
+            self.title = self.bootfs.split('/', 2)[2]
+        except:
+            pass
+
+    def init_from_rootpath(self, rootpath):
+        # Invoke the parent's init_from_rootpath first
+        super(SolarisBootInstance, self).init_from_rootpath(rootpath)
+
+        # self.title is guaranteed to have been initialized to something
+        if not self.title is None:
+            return    # No need to get the title from /etc/release
+
+        try:
+            alt_title = self.bootfs.split('/', 2)[2]
+        except:
+            alt_title = None
+
+        self.title = solaris_release_name(rootpath, alt_title)
+
+
+class SolarisNetBootInstance(SolarisBootInstance):
+    """Abstraction for a Network-based Solaris Boot Instance.
+    """
+    def __init__(self, rootpath, **kwargs):
+        super(SolarisNetBootInstance, self).__init__(rootpath, **kwargs)        
+
+
+class SolarisODDBootInstance(SolarisBootInstance):
+    """Abstraction for an optical-disc-based Solaris Boot Instance
+    """
+    def __init__(self, rootpath, **kwargs):
+        super(SolarisODDBootInstance, self).__init__(rootpath, **kwargs)
+
+    def init_from_rootpath(self, rootpath):
+        # Invoke the parent's init_from_rootpath first
+        super(SolarisBootInstance, self).init_from_rootpath(rootpath)
+
+        # self.title is guaranteed to have been initialized to something
+        if not self.title is None:
+            return
+
+        self.title = solaris_release_name(rootpath, None)
+
+
+def solaris_release_name(rootpath, alt_title):
+    # On a disk-based instance, the title can be derived from the
+    # first line of the /etc/release file, if it exists
+    title = alt_title if not alt_title is None else 'Oracle Solaris'
+    try:
+        with open(rootpath + '/etc/release') as etcrelease:
+            title = etcrelease.readline().strip()
+    except IOError:
+        pass
+
+    return title
+
+
+###############################################################################
+####################################  TESTS  ##################################
+###############################################################################
+
+class TestBootConfig(unittest.TestCase):
+
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        print 'Done with ' + str(self)
+
+    def test_flags(self):
+        pass
+
+def testSuite():
+    return unittest.TestLoader().loadTestsFromTestCase(TestBootConfig)
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootinfo.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,97 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Various classes that provide access to system- and boot-related information
+"""
+
+import logging
+
+from .pysol import devfs_bootdev_get_list
+from . import BootmgmtUnsupportedOperationError
+
+logger = logging.getLogger('bootmgmt')
+
+class SystemFirmware(object):
+    PROP_BOOT_DEVICE = 'boot-device'
+
+    @staticmethod
+    def get(firmware_name=None):
+        from .backend.fw import BackendFWFactory
+        return BackendFWFactory.get(firmware_name)
+
+    def __init__(self, fw_name=None):
+        self._fw_name = fw_name
+
+    @property
+    def fw_name(self):
+        return self._fw_name
+
+    def getprop(self, propname):
+        if not propname == SystemFirmware.PROP_BOOT_DEVICE:
+            raise BootmgmtUnsupportedOperationError('Property not supported')
+        # libdevinfo's devfs_bootdev_get_list returns an ordered list of
+        # boot devices.  The form of the return value is a tuple of tuples:
+        # ((<physpath>, (<logicalpath1>, <logicalpath2>, ...), ...)
+        if propname == SystemFirmware.PROP_BOOT_DEVICE:
+            return devfs_bootdev_get_list()
+
+    def setprop(self, propname, value): pass
+    def delprop(self, propname): pass
+
+###############################################################################
+
+class BootVariables(object):
+    @staticmethod
+    def get(sysroot=None, arch=None, osname='solaris'):
+        if sysroot is None:
+            return None
+        from .backend.bootvars import BackendBootVarsFactory
+        return BackendBootVarsFactory.get(sysroot, arch, osname)
+
+    def __init__(self, sysroot=None):
+        self.dirty = False
+        self._sysroot = sysroot
+        self._vardict = {}
+        if not self._sysroot is None:
+            self._read()
+
+    def _read(self):
+        pass
+
+    def load(self, sysroot):
+        self._sysroot = sysroot
+        self._read()
+
+    def write(self, inst, alt_dir=None):
+        return None
+
+    def getprop(self, propname):
+        return None
+
+    def setprop(self, propname, value):
+        pass
+
+    def delprop(self, propname):
+        pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootloader.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,182 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Boot loader abstraction for pybootmgmt
+"""
+
+from .backend.loader import BackendBootLoaderFactory
+from .bootinfo import SystemFirmware
+from .bootutil import LoggerMixin
+from . import BootmgmtError, BootmgmtNotSupportedError
+from . import BootmgmtUnsupportedPropertyError
+
+class BootLoader(LoggerMixin):
+
+    PROP_MINMEM64 = 'minmem64'
+    PROP_TIMEOUT = 'timeout'
+
+    PROP_CONSOLE = 'console'
+    PROP_CONSOLE_TEXT = 'text'
+    PROP_CONSOLE_GFX = 'graphics'
+    PROP_CONSOLE_SERIAL = 'serial'
+
+    PROP_QUIET = 'quiet'
+
+    PROP_BOOT_TARGS = 'boot-targets'
+
+    # When console=serial, this property should be set:
+    PROP_SERIAL_PARAMS = 'serial_params'
+    # Indices for the serial_params tuple:
+    PROP_SP_PORT = 0
+    PROP_SP_SPEED = 1
+    PROP_SP_DATAB = 2
+    PROP_SP_PARITY = 3
+    PROP_SP_STOPB = 4
+    PROP_SP_FLOWC = 5
+
+
+    @staticmethod
+    def get(**kwargs):
+        return BackendBootLoaderFactory.get(**kwargs)
+
+    @classmethod
+    def probe(cls, **kwargs):
+         return (None, None)
+
+    @property
+    def dirty(self):
+        return self._dirty
+
+    @dirty.setter
+    def dirty(self, value):
+        if not type(value) is bool:
+            raise ValueError('dirty must be a bool')
+        if value != self._dirty:
+            self._debug('dirty => ' + str(value))
+            self._dirty = value
+
+    def __init__(self, **kwargs):
+        self._dirty = False
+        self.old_loader = None
+        self.devices = kwargs.get('devices', None)
+        self.firmware = SystemFirmware.get(kwargs.get('fwtype', None))
+        self.version = None
+        self._boot_config = kwargs.get('bootconfig', None)
+        self._bl_props = {}
+
+        # If there is a root dir defined in the bootconfig passed in,
+        # and no rootpath, set rootpath to the bootconfig's root.
+        bc_sysroot = '/'
+        if (not self._boot_config is None and
+            not self._boot_config.get_root() is None):
+             bc_sysroot = self._boot_config.get_root()
+
+        self.rootpath = kwargs.get('rootpath', bc_sysroot)
+        
+
+    def _write_config(self, basepath):
+         pass
+
+    def load_config(self):
+         pass
+
+    def new_config(self):
+         pass
+
+    def migrate_config(self):
+         pass
+
+    # Property-related methods
+    def _prop_validate(self, key, value=None, validate_value=False):
+        props = getattr(self, 'SUPPORTED_PROPS', [])
+        if not key in props:
+            raise BootmgmtUnsupportedPropertyError(key + ' is not a supported '
+                                                   'property')
+
+    def setprop(self, key, value):
+         pass
+
+    def getprop(self, key):
+         pass
+
+    def delprop(self, key):
+         pass
+
+    def install(self, location):
+        """Install the boot loader onto a disk or set of disks.  location
+        is either a single string or a list of strings (each of which
+        is a character-special device path, which the boot loader
+        will use to determine where to install itself) or a path to a
+        directory that will hold the boot loader files and
+        configuration files (see below).
+        If the version property is set in this BootLoader instance and
+        this boot loader supports recording the version number at boot 
+        loader installation time, each location specified will be queried
+        for boot loader version information.  
+        The boot loader will be installed to each installation location
+        with a version older than the version specified.
+
+        Note that for some boot configurations (i.e. net and ODD),
+        the boot loader isn't installed per-se;  the loader and its
+        support files are copied to a well-known location, and that
+        location is used as an input to other programs or configuration
+        stores (i.e. mkisofs is given the path to a boot image; the
+        DHCP server is configured with the name of the NBP (i.e. pxegrub)
+        and configuration file(s) for PXE-compliant boot environments,
+        etc.).  For these cases, install() will return a set of tuples,
+        each of the following form:
+           (<type>, <srcpath>, <dstpath>, <owner>, <group>, <mode>)
+        <type> depends on the platform and boot configuration that this
+        BootLoader is associated with (defined in child classes).
+        <srcpath> is the path from which the file should be copied.
+        <dstpath> is the path to which the file should be copied (tokens
+        are embedded in the path to instruct the consumer of the final
+        location (see the commit_boot_config() documentation in children
+        of BootConfig for a list of tokens and their expansions)).
+        <owner>, <group>, and <mode> are the uid, gid, and permissions
+        mode bits (respectively) that should be set on the destination
+        file.  
+        """
+        pass
+
+
+#
+# BootLoader Exceptions
+#
+
+class BootLoaderInstallError(BootmgmtError):
+    pass
+
+class BootPartitionAccessError(BootLoaderInstallError):
+    pass
+
+class BootDeviceNotFoundError(BootLoaderInstallError):
+    pass
+
+class BootLoaderFeatureUnsupportedError(BootmgmtNotSupportedError):
+    pass
+
+class BootLoaderUnsupportedPartTypeError(BootLoaderFeatureUnsupportedError):
+    pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/bootutil.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,52 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""
+Various support code for the bootmgmt package
+"""
+
+import logging
+import platform
+import os
+
+from . import BootmgmtNotSupportedError
+
+def get_current_arch_string():
+    proc = platform.processor()
+    if proc == 'i386':
+        return 'x86'
+    elif proc == 'sparc':
+        return 'sparc'
+    raise BootmgmtNotSupportedError('Unsupported platform: ' + proc)
+
+class LoggerMixin(object):
+
+    logger = logging.getLogger('bootmgmt')
+
+    if not os.environ.get('BOOTMGMT_DEBUG', None) is None:
+        logging.basicConfig(level=logging.DEBUG)
+
+    @classmethod
+    def _debug(cls, log_msg):
+        LoggerMixin.logger.debug(cls.__name__ + ': ' + log_msg)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/bootmgmt/pysol.py	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,650 @@
+#! /usr/bin/python2.6
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+"Solaris-specific library wrappers and functions"
+
+from ctypes import *
+import gettext
+import errno
+import platform
+import os
+
+_ = gettext.translation("SUNW_OST_OSLIB", "/usr/lib/locale",
+    fallback=True).gettext
+
+(LIBZFS_INIT_FAILURE,
+ ) = range(1)
+
+_msgs = {
+    LIBZFS_INIT_FAILURE: _('libzfs initialization failure')
+}
+
+#
+# From sys/fstyp.h
+#
+FSTYPSZ = 16
+
+_libc = CDLL('libc.so', use_errno=True)
+_platwidth = platform.architecture()[0][:2]
+_libc.fopen.restype = c_void_p
+_libc.free.argtypes = [ c_void_p ]
+_libc.free.restype = None
+_statvfs_syscall = 'statvfs' + ('64' if _platwidth == '32' else '')
+# print 'platwidth = `%s\' | _syscall = `%s\'' % (_platwidth, _syscall)
+_statvfs = getattr(_libc, _statvfs_syscall)
+
+
+def ctype2dict(ctype_struct_instance):
+	dictresult = {}
+
+	# A very important side-effect of calling getattr() here is
+	# that the ctypes API creates copies of the data we're
+	# retrieving from the "structure".  If it did not do that,
+	# calling libc APIs that use static data areas would cause
+	# the resulting Python strings to refer to the same storage
+	# area.
+	for x in [x for (x, y) in ctype_struct_instance._fields_]:
+		dictresult[x] = getattr(ctype_struct_instance, x)
+
+	return dictresult
+
+# ==============================[ statvfs ]===============================
+
+class StatVFS_Result(Structure):
+	"""From the manpage:
+	int statvfs(const char *restrict path, struct statvfs *restrict buf);
+
+		u_long      f_bsize;     /* preferred file system block size */
+		u_long      f_frsize;    /* fundamental filesystem block
+		                                  (size if supported) */
+		fsblkcnt_t  f_blocks;    /* total # of blocks on file system
+		                                  in units of f_frsize */
+		fsblkcnt_t  f_bfree;     /* total # of free blocks */
+		fsblkcnt_t  f_bavail;    /* # of free blocks avail to
+			                          non-privileged user */
+		fsfilcnt_t  f_files;     /* total # of file nodes (inodes) */
+		fsfilcnt_t  f_ffree;     /* total # of free file nodes */
+		fsfilcnt_t  f_favail;    /* # of inodes avail to
+		                                  non-privileged user*/
+		u_long      f_fsid;      /* file system id (dev for now) */
+		char        f_basetype[FSTYPSZ]; /* target fs type name,
+		     	                           null-terminated */
+		u_long      f_flag;      /* bit mask of flags */
+		u_long      f_namemax;   /* maximum file name length */
+		char        f_fstr[32];  /* file system specific string */
+	  if not 64-bit process
+	       u_long      f_filler[16]; /* reserved for future expansion */
+	  endif
+
+	We always use the 64-bit statvfs variant (statvfs64 in 32-bit
+	processes and statvfs in 64-bit processes)
+
+	"""
+
+	_fields_ = [	("f_bsize", c_ulong),
+			("f_frsize", c_ulong),
+			("f_blocks", c_ulonglong),
+			("f_bfree", c_ulonglong),
+			("f_bavail", c_ulonglong),
+			("f_files", c_ulonglong),
+			("f_ffree", c_ulonglong),
+			("f_favail", c_ulonglong),
+			("f_fsid", c_ulong),
+			("f_basetype", c_char * FSTYPSZ),
+			("f_flag", c_ulong),
+			("f_namemax", c_ulong),
+			("f_fstr", c_char * 32),
+			("f_filler", c_int * 16)	]
+
+
+def statvfs(path):
+	"""Returns a dictionary whose members are the result of the Solaris
+	statvfs() call"""
+
+	result = StatVFS_Result()
+
+	if _statvfs(path, pointer(result)) != 0:
+		raise IOError(get_errno(), os.strerror(get_errno()))
+
+	return ctype2dict(result)
+
+
+# ===========================[ mnttab functions ]============================
+
+
+class SolarisMntTab(Structure):
+	"Python ctype expression of the Solaris mnttab structure"
+	_fields_ = [('mnt_special', c_char_p),
+		    ('mnt_mountp', c_char_p),
+		    ('mnt_fstype', c_char_p),
+		    ('mnt_mntopts', c_char_p),
+		    ('mnt_time', c_char_p)]
+
+class SolarisExtMntTab(Structure):
+	"Python ctype expression of the Solaris extmnttab structure"
+	_fields_ = [('mnt_special', c_char_p),
+		    ('mnt_mountp', c_char_p),
+		    ('mnt_fstype', c_char_p),
+		    ('mnt_mntopts', c_char_p),
+		    ('mnt_time', c_char_p),
+		    ('mnt_major', c_uint),
+		    ('mnt_minor', c_uint)]
+
+def mnttab_err_decode(code):
+	"""Decodes the following error codes from mnttab.h:
+	#define MNT_TOOLONG     1       /* entry exceeds MNT_LINE_MAX */
+	#define MNT_TOOMANY     2       /* too many fields in line */
+	#define MNT_TOOFEW      3       /* too few fields in line */
+	"""
+	if code == 1:
+		return 'Entry exceeds 1024 characters'
+	elif code == 2:
+		return 'Too many fields in line'
+	elif code == 3:
+		return 'Too few fields in line'
+	else:
+		return 'Unknown mnttab error'
+
+
+def mnttab_open(mtab='/etc/mnttab'):
+	global _mnttab_FILE
+	_mnttab_FILE = c_void_p(_libc.fopen(mtab, 'r'))
+	if _mnttab_FILE.value is None:
+		raise IOError(get_errno(), mtab + ': ' + 
+                              os.strerror(get_errno()))
+
+def getmntent():
+	"""Returns the next mnttab entry as a dictionary whose keys
+	are:
+	        mnt_special
+	        mnt_mountp
+	        mnt_fstype
+	        mnt_mntopts
+	        mnt_time
+	or None if there are no more entries
+	"""
+	mntent = SolarisMntTab()
+	r = _libc.getmntent(_mnttab_FILE, byref(mntent))
+	if r < 0:		# EOF
+		return None
+	elif r > 0:		# Error
+		raise IOError(r, mnttab_err_decode(r))
+
+	return ctype2dict(mntent)
+
+
+def getmntany(**attrs):
+	"""Returns a mnttab entry matching the attributes passed in, or
+	None if no entry matches.
+	"""
+	mntent = SolarisMntTab()
+	mntmatch = SolarisMntTab()
+	for x in attrs.keys():
+		mntent.__setattr__(x, attrs[x])
+	r = _libc.getmntany(_mnttab_FILE, byref(mntmatch), byref(mntent))
+	if r < 0:		# EOF
+		return None
+	elif r > 0:
+		raise IOError(r, mnttab_err_decode(r))
+
+	return ctype2dict(mntmatch)
+
+
+def getextmntent():
+	"""Returns the next extmnttab entry as a dictionary whose keys
+	are:
+	        mnt_special
+	        mnt_mountp
+	        mnt_fstype
+	        mnt_mntopts
+	        mnt_time
+		mnt_major
+		mnt_minor
+	or None if there are no more entries.
+	"""
+	extmnt = SolarisExtMntTab()
+	r = _libc.getextmntent(_mnttab_FILE, byref(extmnt), sizeof(extmnt))
+	if r < 0:		# EOF
+		return None
+	elif r > 0:
+		raise IOError(r, mnttab_err_decode(r))
+
+	return ctype2dict(extmnt)
+
+
+def resetmnttab(reopen=False):
+	"""Rewinds the mnttab file to the beginning
+	if reopen is True, the mnttab file is closed, then reopened
+	"""
+	if reopen is True:
+		mnttab_close()
+		mnttab_open()
+	else:
+		_libc.rewind(_mnttab_FILE)
+
+		
+def mnttab_close():
+	_libc.fclose(_mnttab_FILE)
+
+
+# ==============================[devlink walking]============================
+
+_libdi = CDLL('libdevinfo.so', use_errno=True)
+_libdi.di_devlink_init.restype = c_void_p
+_libdi.di_devlink_init.argtypes = [ c_char_p, c_int ]
+_libdi.di_devlink_path.restype = c_char_p
+_libdi.di_devlink_path.argtypes = [ c_void_p ]
+# The walker function returns an int and takes 2 void *'s: the di_devlink_t,
+# and the arg
+_devlink_walkfunc_type = CFUNCTYPE(c_int, c_void_p, c_void_p)
+_libdi.di_devlink_walk.argtypes = [ c_void_p, c_char_p, c_char_p, c_int,
+				    c_void_p, _devlink_walkfunc_type ]
+# from <libdevinfo.h>:
+DI_MAKE_LINK = 1
+DI_PRIMARY_LINK = 1
+DI_SECONDARY_LINK = 2
+DI_WALK_CONTINUE = 0
+DI_WALK_TERMINATE = -3
+
+def di_devlink_init(drvname=None, flags=0):
+	"""Initialize the libdevinfo devlink interfaces"""
+	hdl = c_void_p(_libdi.di_devlink_init(drvname, flags))
+	if hdl.value is None:
+		raise IOError(get_errno(), os.strerror(get_errno()))
+	return hdl
+
+
+def di_devlink_path(dl):
+	"""Return a string that is the path corresponding to the devlink
+	passed in
+	"""
+	r = _libdi.di_devlink_path(dl)
+	if r is None:
+		raise IOError(get_errno(), os.strerror(get_errno()))
+	return r
+
+
+def di_devlink_walk(hdl, pattern, path, flags, walk_arg, walkfunc):
+	"""Conduct a walk of all devlinks that patch the given pattern and
+	that pertain to the given path.  Note that since ctypes passes
+	arguments to callbacks as 'int's, those arguments must be converted
+	into a useful Python type (passing a string, then reconstituting
+	a list from that string, for example).
+	"""
+
+	wf = _devlink_walkfunc_type(walkfunc)
+	r = _libdi.di_devlink_walk(hdl, pattern, path, flags, walk_arg, wf)
+	if r < 0:
+		raise IOError(get_errno(), os.strerror(get_errno()))
+	
+
+def di_devlink_fini(hdl):
+	"""Performs cleanup after use of the devlink interfaces"""
+	_libdi.di_devlink_fini(hdl)
+
+# ==========================[ libdevinfo subset ]=============================
+
+_minor_walkfunc_type = CFUNCTYPE(c_int, c_void_p, c_void_p, c_void_p)
+
+_libdi.di_init.restype = c_void_p
+_libdi.di_init.argtypes = [ c_char_p, c_int ]
+_libdi.di_fini.argtypes = [ c_void_p ]
+_libdi.di_devfs_path.restype = c_void_p
+_libdi.di_devfs_path.argtypes = [ c_void_p ]
+_libdi.di_devfs_path_free.argtypes = [ c_void_p ]
+_libdi.di_minor_spectype.argtypes = [ c_void_p ]
+_libdi.di_minor_name.restype = c_char_p
+_libdi.di_minor_name.argtypes = [ c_void_p ]
+_libdi.di_minor_next.restype = c_void_p
+_libdi.di_minor_next.argtypes = [ c_void_p, c_void_p ]
+_libdi.di_walk_minor.argtypes = [ c_void_p, c_char_p, c_int, c_void_p,
+					_minor_walkfunc_type ]
+_libdi.di_prop_lookup_strings.argtypes = [ c_ulong, c_void_p,
+                                           c_char_p, c_void_p ]
+class struct_boot_dev(Structure):
+    _fields_ = [('bootdev_element', c_char_p),
+                ('bootdev_trans', POINTER(c_char_p))]
+pp_struct_boot_dev = POINTER(POINTER(struct_boot_dev))
+_libdi.devfs_bootdev_get_list.argtypes = [ c_char_p, POINTER(pp_struct_boot_dev) ]
+_libdi.devfs_bootdev_free_list.argtypes = [ pp_struct_boot_dev ]
+_libdi.devfs_bootdev_free_list.restype = None
+
+
+S_IFBLK = 0x6000					# from <sys/stat.h>
+DDI_NT_BLOCK = "ddi_block"				# from <sys/sunddi.h>
+
+# from <sys/devinfo_impl.h>:
+DIIOC = (0xdf << 8)
+DINFOSUBTREE = (DIIOC | 0x01)
+DINFOMINOR = (DIIOC | 0x02)
+DINFOPROP = (DIIOC | 0x04)
+DINFOPATH = (DIIOC | 0x08)
+DINFOCPYALL = (DINFOSUBTREE | DINFOPROP | DINFOMINOR)
+
+# from <sys/ddipropdefs.h>
+DDI_DEV_T_ANY = -2
+DDI_DEV_T_NONE = -1
+
+def _di_minor_spectype(minor):
+	"""Returns the type (block or char) of the special node whose minor
+	info is passed in
+	"""
+	return _libdi.di_minor_spectype(minor)
+
+
+def di_init(path='/', flags=(DINFOPATH|DINFOCPYALL)):
+	"""Initialize the device tree snapshot, starting from the device path
+	specified.  flags are (DINFOPATH|DINFOCPYALL) by default.
+	"""
+	hdl = c_void_p(_libdi.di_init(path, flags))
+	if hdl.value is None:
+		raise IOError(get_errno(), os.strerror(get_errno()))
+	return hdl
+
+
+def di_minor_is_block(minor):
+	return (True if _di_minor_spectype(minor) == S_IFBLK else False)
+
+
+def di_minor_name(minor):
+	"Returns the string name of the minor passed in"
+	return _libdi.di_minor_name(minor)
+
+
+def di_devfs_path(node):
+	"Returns the string name of the node passed in"
+	r = c_void_p(_libdi.di_devfs_path(node))
+	if r.value is None:
+		raise IOError(get_errno(), os.strerror(get_errno()))
+	rs = c_char_p(r.value).value
+	_libdi.di_devfs_path_free(r)
+	return rs
+
+
+def di_minor_next(node, minor=None):
+	"""Returns the next minor node, relative to the minor passed in.
+	If minor isn't specified, or is passed in as None, the first minor
+	associated with the node is returned.  None is returned when no
+	more minors exist.
+	"""
+	r = c_void_p(_libdi.di_minor_next(node, minor))
+	if r.value is None:
+		e = get_errno()
+		# NXIO is a special case-- an indicator of no more minors
+		if e == errno.ENXIO:
+			return None
+		else:
+			raise IOError(e, os.strerror(e))
+	return r
+
+def di_walk_minor(rootnode, minortype, walkarg, walkfunc, flag=0):
+	"""Perform a walk of all minor nodes attached to device nodes
+	in a subtree rooted at `root'.  walkargs should be a simple Python
+	type, since it will need to be reconstituted in the walkfunc callback.
+	"""
+
+	wf = _minor_walkfunc_type(walkfunc)
+	r = _libdi.di_walk_minor(rootnode, minortype, flag, walkarg, wf)
+	print r
+	if r < 0:
+		raise IOError(get_errno(), os.strerror(get_errno()))
+
+
+def di_fini(hdl):
+	_libdi.di_fini(hdl)
+
+
+def devfs_bootdev_get_list():
+
+	bdl = pp_struct_boot_dev()
+
+	rv = _libdi.devfs_bootdev_get_list('/', pointer(bdl))
+
+        # No exception is raised on error, because there is no errno
+        # value that accompanies the failure
+        if rv < 0 or bool(bdl) is False:
+		return None
+
+	i = 0
+        bootdevs = []
+        while not bool(bdl[i]) is False:
+		physical = bdl[i][0].bootdev_element
+		j = 0
+                logicals = []
+		while not bool(bdl[i][0].bootdev_trans[j]) is False:
+			logicals.append(bdl[i][0].bootdev_trans[j])
+                        j += 1
+                bootdevs.append((physical, tuple(logicals)))
+		i += 1
+
+        _libdi.devfs_bootdev_free_list(bdl)
+
+        return tuple(bootdevs)
+
+
+def di_prop_lookup_strings(dev, node, prop_name, prop_data):
+    rv = _libdi.di_prop_lookup_strings(dev, node, prop_name, prop_data)
+    if rv < 0:
+        raise IOError(get_errno(), os.strerror(get_errno()))
+    return rv
+
+def di_find_root_prop(propname):
+    return di_find_prop(propname, '/')
+
+def di_find_prop(propname, root):
+    hdl = di_init(root, DINFOPROP)
+    propval = None
+    try:
+        # This is a bit tricky, sicne di_prop_lookup_strings returns ONE
+        # string with several embedded NUL terminators (if multiple strings
+        # are returned).  To deal with this in ctypes, we use a pointer to
+        # a c_void_p as the last parameter and manually pull out each string
+        # To pull out each string, we cast the c_void_p to a c_char_p, which
+        # automatically gets us the first string (ctypes extracts it for us)
+        # To get subsequent strings, we need to manually index the pointer,
+        # adding the length of the previous string + 2 (one to get us to the
+        # NUL and one more to get us past it).
+        value = pointer(c_void_p())
+        rv = di_prop_lookup_strings(DDI_DEV_T_ANY, hdl, propname,
+                                    value)
+        stringvalue = cast(value.contents, c_char_p)
+        if rv == 1:
+            propval = stringvalue.value
+        elif rv > 1:
+            stringlen = len(stringvalue.value)
+            propval = [stringvalue.value]
+            rv -= 1
+            while rv > 0:
+                stringvalue = cast(c_voidp(value.contents.value +
+                                           stringlen + 2),
+                                   c_char_p)
+                propval.append(stringvalue.value)
+                stringlen = len(stringvalue.value)
+                rv -= 1
+    except IOError as e:
+        if e.errno == errno.ENXIO:
+            propval = None
+        else:
+            raise
+    finally:
+        di_fini(hdl)
+    return propval
+
+
+# ==============================[ libfstyp ]==================================
+
+_libfstyp = CDLL('libfstyp.so.1', use_errno=True)
+_libfstyp.fstyp_init.argtypes = [ c_int, c_uint64, c_char_p, c_void_p ]
+_libfstyp.fstyp_ident.argtypes = [ c_void_p, c_char_p, c_void_p ]
+_libfstyp.fstyp_fini.argtypes = [ c_void_p ]
+_libfstyp.fstyp_strerror.argtypes = [ c_void_p, c_int ]
+_libfstyp.fstyp_strerror.restype = c_char_p
+
+def fstyp_init(fd, offset=0, module_dir=None):
+	"Returns a handle that can be used with other fstyp functions"
+	handle = c_void_p()
+	r = _libfstyp.fstyp_init(fd, offset, module_dir, byref(handle))
+	if r != 0:
+		raise IOError(r, _libfstyp.fstyp_strerror(r))
+	return handle
+
+def fstyp_ident(handle, name=None):
+	result = c_char_p(0)
+	r = _libfstyp.fstyp_ident(handle, name, byref(result))
+	if r == 1:		# No Match Found
+		return None
+	elif r != 0:
+		raise IOError(r, _libfstyp.fstyp_strerror(handle, r))
+	return result.value
+
+def fstyp_fini(handle):
+	_libfstyp.fstyp_fini(handle)
+
+
+# ================================[ isalist ]=================================
+
+SI_ISALIST = 514     # return supported isa list
+ISALIST_LEN = 257    # max buffer size, as per manpage
+
+_libc.sysinfo.argtypes = [ c_int, c_char_p, c_long ]
+_libc.sysinfo.restype = c_int
+
+def isalist():
+	"Returns a list of ISAs supported on the currently-running system"
+
+	b = create_string_buffer(ISALIST_LEN)
+	r = _libc.sysinfo(SI_ISALIST, b, ISALIST_LEN)
+	if r < 0:
+		raise OSError(get_errno(), os.strerror(get_errno()))
+	return b.value.split()
+
+
+# =================================[ libscf ]=================================
+
+SCF_STATE_STRING_ONLINE = 'online'
+
+_libscf = CDLL('libscf.so.1', use_errno=True)
+_libscf.smf_get_state.argtypes = [ c_char_p ]
+_libscf.smf_get_state.restype = c_void_p  # Why c_void_p ? See comment below.
+_libscf.smf_enable_instance.argtypes = [ c_char_p, c_int ]
+_libscf.smf_enable_instance.restype = c_int
+_libscf.scf_error.argtypes = None
+_libscf.scf_error.restype = c_int
+_libscf.scf_strerror.argtypes = [ c_int ]
+_libscf.scf_strerror.restype = c_char_p
+
+
+class SCFError(Exception):
+    "Wrap for smf errors"
+
+    def __init__(self, errcode, errstring):
+        self.errno = errcode
+        self.strerror = errstring
+
+
+def smf_get_state(svc):
+    "Returns the current state of the specified service"
+
+    # Need to store the result in a c_void_p because if a c_char_p were
+    # specified as the return type, it would be automatically converted
+    # to a Python string and we'd love the original pointer value, which
+    # we need for the call to free()
+
+    alloced_str = c_void_p(_libscf.smf_get_state(svc))
+    if alloced_str.value == 0:
+        return None
+    result = c_char_p(alloced_str.value).value
+    _libc.free(alloced_str)
+    return result
+
+
+def smf_enable_instance(svc, flags=0):
+    "Enables the given smf service with the specified flags"
+
+    ret = _libscf.smf_enable_instance(svc, flags)
+    if ret == -1:
+        scferr = _libscf.scf_error()
+        raise SCFError(scferr, _libscf.scf_strerror(scferr))
+    else:
+        return ret
+
+
+# =================================[ libzfs ]=================================
+
+_libzfs = CDLL('libzfs.so.1', use_errno=True)
+_libzfs.libzfs_init.restype = c_void_p
+_libzfs.zfs_open.restype = c_void_p
+_libzfs.zfs_open.argtypes = [ c_void_p, c_char_p, c_int ]
+_libzfs.zfs_close.argtypes = [ c_void_p ]
+zif_callback = CFUNCTYPE(c_int, c_void_p, c_void_p)
+_libzfs.zfs_iter_filesystems.argtypes = [ c_void_p, zif_callback, c_void_p ]
+
+ZFS_TYPE_FILESYSTEM = 1
+ZFS_TYPE_SNAPSHOT = 2
+ZFS_TYPE_VOLUME = 4
+ZFS_TYPE_POOL = 8
+
+def libzfs_init():
+	hdl = _libzfs.libzfs_init()
+	if hdl.value is None:
+		raise IOError(0, _msgs[LIBZFS_INIT_FAILURE])
+	return hdl
+
+def zfs_open(handle, devpath, type=ZFS_TYPE_FILESYSTEM): pass
+
+def libzfs_fini(handle):
+	_libzfs.libzfs_fini(handle)
+
+
+__all__ = [ "statvfs",
+	    "mnttab_open",
+	    "mnttab_close",
+	    "getmntent",
+	    "getmntany",
+	    "getextmntent",
+	    "resetmnttab",
+	    "DI_MAKE_LINK",
+	    "DI_PRIMARY_LINK",
+	    "DI_SECONDARY_LINK",
+	    "DI_WALK_CONTINUE",
+	    "DI_WALK_TERMINATE",
+	    "di_devlink_init",
+	    "di_devlink_path",
+	    "di_devlink_walk",
+	    "di_devlink_fini",
+	    "DDI_NT_BLOCK",
+	    "DINFOPATH",
+	    "DINFOCPYALL",
+	    "di_init",
+	    "di_minor_is_block",
+	    "di_minor_name",
+	    "di_devfs_path",
+	    "di_minor_next",
+	    "di_walk_minor",
+	    "di_fini",
+            "devfs_bootdev_get_list",
+            "fstyp_init",
+            "fstyp_ident",
+            "fstyp_fini",
+            "isalist" ]
--- a/usr/src/pkg/Makefile	Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/pkg/Makefile	Thu Apr 21 23:16:03 2011 -0600
@@ -197,7 +197,8 @@
 # complete reprocessing of all manifests because they'll fail command
 # dependency checking.
 #
-PM_TRANSFORMS= publish restart_fmri defaults extract_metadata
+PM_TRANSFORMS= publish restart_fmri defaults extract_metadata \
+		disable_depend_checks
 PM_INC= transforms
 
 PKGMOG_DEFINES= \
--- a/usr/src/pkg/manifests/system-library-install.mf	Thu Apr 21 15:28:20 2011 -0600
+++ b/usr/src/pkg/manifests/system-library-install.mf	Thu Apr 21 23:16:03 2011 -0600
@@ -31,6 +31,7 @@
 dir path=usr group=sys
 dir path=usr/lib
 dir path=usr/lib/python2.6
+dir path=usr/lib/python2.6/vendor-packages/bootmgmt
 dir path=usr/lib/python2.6/vendor-packages
 dir path=usr/lib/python2.6/vendor-packages/osol_install
 dir path=usr/lib/python2.6/vendor-packages/solaris_install
@@ -195,6 +196,54 @@
 file path=usr/lib/python2.6/vendor-packages/solaris_install/transfer/prog.pyc
 file path=usr/lib/python2.6/vendor-packages/solaris_install/transfer/svr4.py
 file path=usr/lib/python2.6/vendor-packages/solaris_install/transfer/svr4.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootarchive.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootarchive.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootconfig.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootconfig.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootinfo.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootinfo.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootloader.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootloader.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootutil.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/bootutil.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/pysol.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/pysol.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/autogen/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/autogen/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/autogen/solaris.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/autogen/solaris.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/sparc/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/sparc/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/sparc/solaris.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/sparc/solaris.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/x86/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/x86/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/x86/solaris.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/bootvars/x86/solaris.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/bios.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/bios.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/obp.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/obp.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/uefi64.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/fw/uefi64.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/__init__.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/grub2.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/grub2.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/legacygrub.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/legacygrub.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/menulst.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/menulst.pyc
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/sbb.py
+file path=usr/lib/python2.6/vendor-packages/bootmgmt/backend/loader/sbb.pyc
 file path=usr/share/install/ai.dtd mode=0444 group=sys
 file path=usr/share/install/configuration.dtd mode=0444 group=sys
 file path=usr/share/install/dc.dtd mode=0444 group=sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/transforms/disable_depend_checks	Thu Apr 21 23:16:03 2011 -0600
@@ -0,0 +1,26 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+# This is a workaround the existing bug in pkgdepend - 18194
+<transform file path=usr/lib/python2.6/vendor-packages/bootmgmt/.+ -> default pkg.depend.bypass-generate .*>
+