17165 - Implement ManifestParser / ManifestWriter components for Installer
authorDermot McCluskey <dermot.mccluskey@oracle.com>
Fri, 29 Oct 2010 13:35:45 +0000
changeset 910 1dfb931f6b19
parent 909 ab25dde8919d
child 911 08be50491eb1
17165 - Implement ManifestParser / ManifestWriter components for Installer
usr/src/Makefile.master
usr/src/Targetdirs
usr/src/lib/Makefile
usr/src/lib/Makefile.targ
usr/src/lib/install_manifest/Makefile
usr/src/lib/install_manifest/common/Makefile
usr/src/lib/install_manifest/common/__init__.py
usr/src/lib/install_manifest/parser/Makefile
usr/src/lib/install_manifest/parser/parser.py
usr/src/lib/install_manifest/test/common.py
usr/src/lib/install_manifest/test/manifests/configuration.dtd
usr/src/lib/install_manifest/test/manifests/doc-to-manifest.xslt
usr/src/lib/install_manifest/test/manifests/execution.dtd
usr/src/lib/install_manifest/test/manifests/manifest.dtd
usr/src/lib/install_manifest/test/manifests/manifest_invalid.xml
usr/src/lib/install_manifest/test/manifests/manifest_no_dtd_ref.xml
usr/src/lib/install_manifest/test/manifests/manifest_parse_error.xml
usr/src/lib/install_manifest/test/manifests/manifest_simple.xml
usr/src/lib/install_manifest/test/manifests/manifest_simple_dtd_path.xml
usr/src/lib/install_manifest/test/manifests/manifest_syntax_error.xml
usr/src/lib/install_manifest/test/manifests/manifest_xinclude_main.xml
usr/src/lib/install_manifest/test/manifests/manifest_xinclude_target.xml
usr/src/lib/install_manifest/test/manifests/software.dtd
usr/src/lib/install_manifest/test/manifests/target.dtd
usr/src/lib/install_manifest/test/test_mp_invalid_params.py
usr/src/lib/install_manifest/test/test_mp_load_manifest.py
usr/src/lib/install_manifest/test/test_mp_with_engine.py
usr/src/lib/install_manifest/test/test_mp_without_engine.py
usr/src/lib/install_manifest/test/test_mw_dry_run.py
usr/src/lib/install_manifest/test/test_mw_invalid_params.py
usr/src/lib/install_manifest/test/test_mw_with_engine.py
usr/src/lib/install_manifest/test/test_mw_without_engine.py
usr/src/lib/install_manifest/writer/Makefile
usr/src/lib/install_manifest/writer/writer.py
usr/src/pkg/manifests/system-library-install.mf
usr/src/tools/tests/tests.nose
--- a/usr/src/Makefile.master	Thu Oct 28 15:10:27 2010 -0700
+++ b/usr/src/Makefile.master	Fri Oct 29 13:35:45 2010 +0000
@@ -105,6 +105,7 @@
 ROOTPYTHONVENDORSOLINSTALLENGINE =	$(ROOTPYTHONVENDORSOLINSTALL)/engine
 ROOTPYTHONVENDORSOLINSTALLENGINETEST =  $(ROOTPYTHONVENDORSOLINSTALLENGINE)/test
 ROOTPYTHONVENDORSOLINSTALLTARGET =	$(ROOTPYTHONVENDORSOLINSTALL)/target
+ROOTPYTHONVENDORSOLINSTALLMANIFEST=	$(ROOTPYTHONVENDORSOLINSTALL)/manifest
 ROOTAUTOINST=		$(ROOT)/usr/share/auto_install
 ROOTAUTOINSTSCPROFILES=	$(ROOTAUTOINST)/sc_profiles
 ROOTSBIN=		$(ROOT)/sbin
--- a/usr/src/Targetdirs	Thu Oct 28 15:10:27 2010 -0700
+++ b/usr/src/Targetdirs	Fri Oct 29 13:35:45 2010 +0000
@@ -72,6 +72,7 @@
 	/usr/lib/python2.6/vendor-packages/solaris_install/engine \
 	/usr/lib/python2.6/vendor-packages/solaris_install/engine/test \
 	/usr/lib/python2.6/vendor-packages/solaris_install/target \
+	/usr/lib/python2.6/vendor-packages/solaris_install/manifest \
 	/usr/sbin \
 	/usr/share/auto_install \
 	/usr/share/auto_install/sc_profiles \
--- a/usr/src/lib/Makefile	Thu Oct 28 15:10:27 2010 -0700
+++ b/usr/src/lib/Makefile	Fri Oct 29 13:35:45 2010 +0000
@@ -51,7 +51,8 @@
                 install_engine \
                 install_logging \
                 install_logging_pymod \
-                install_target
+                install_target \
+		install_manifest
 
 ADMINLIBRARIES=	libadmldb.so libadmutil.so
 ROOTADMINLIBLINKS=	$(ADMINLIBRARIES:%=$(ROOTADMINLIB)/%)
--- a/usr/src/lib/Makefile.targ	Thu Oct 28 15:10:27 2010 -0700
+++ b/usr/src/lib/Makefile.targ	Fri Oct 29 13:35:45 2010 +0000
@@ -124,6 +124,9 @@
 $(ROOTPYTHONVENDORSOLINSTALLTARGET):
 	$(INS.dir)
 
+$(ROOTPYTHONVENDORSOLINSTALLMANIFEST):
+	$(INS.dir)
+
 #
 # Python .py and .pyc files need to be installed with the original
 # timestamp of the file preserved. Otherwise, .pyc files will
@@ -149,6 +152,9 @@
 $(ROOTPYTHONVENDORSOLINSTALLTARGET)/%: %
 	$(CP_P.file)
 
+$(ROOTPYTHONVENDORSOLINSTALLMANIFEST)/%: %
+	$(CP_P.file)
+
 $(ROOTRNGSCHEMA)/%: %
 	$(INS.file)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/Makefile	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,55 @@
+#
+##
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+
+include $(SRC)/Makefile.master
+
+SUBDIRS=	common parser writer
+
+.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
+
+.KEEP_STATE:
+
+all check clean clobber lint: $(SUBDIRS)
+
+install: $(SUBDIRS)
+
+headers: 
+
+install_h:	$(SUBDIRS)
+
+$(SUBDIRS): FRC
+	cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/common/Makefile	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,63 @@
+#
+##
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+
+PYMODS		= __init__.py
+
+PYCMODS		= $(PYMODS:%.py=%.pyc)
+
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORSOLINSTALLMANIFEST)/%)
+
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORSOLINSTALLMANIFEST)/%)
+
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+include ../../Makefile.lib
+
+static:
+
+dynamic:
+
+python:
+	$(PYTHON) -m compileall -l $(@D)
+
+all:		python
+
+install_h:
+
+install:	all .WAIT \
+		$(ROOTPYTHONVENDOR) \
+		$(ROOTPYTHONVENDORSOLINSTALL) \
+		$(ROOTPYTHONVENDORSOLINSTALLMANIFEST) \
+		$(ROOTPYMODS) $(ROOTPYCMODS)
+
+lint:		lint_SRCS
+
+FRC:
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/common/__init__.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,104 @@
+#
+##
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""init module for ManifestParser and ManifestWriter"""
+
+import sys
+
+from lxml import etree
+
+
+class ManifestError(Exception):
+    '''
+        Raised if an error occurs during ManifestParser or ManifestWriter.
+
+        Instances of this class have the following attributes:
+        - msg (mandatory), a text string
+        - orig_exception (optional), another Exception object
+          This attribute is used to encapsulate another exception
+    '''
+
+    def __init__(self, msg, orig_exception=None):
+        Exception.__init__(self)
+        self.msg = msg
+        self.orig_exception = orig_exception
+        self.orig_traceback = sys.exc_info()[2]
+
+    def __str__(self):
+        msg = self.msg
+
+        if self.orig_exception is not None:
+            # Return a useful message that includes the original msg,
+            # the name of the encapsulated exception's class (eg
+            # "IOError" or "XMLSyntaxError") and the the msg from
+            # the encapsulated exception.
+            msg = "%s : %s - %s" % (msg, 
+                self.orig_exception.__class__.__name__,
+                self.orig_exception)
+
+        return msg
+
+
+def validate_manifest(tree, dtd_file, logger):
+    '''
+        Validates the given XML tree against the given DTD.
+
+        This is a common function used by ManifestParser and ManifestWriter.
+
+        Parameters:
+        - tree, an etree.ElementTree
+        - dtd_file, the path to a DTD file
+        - logger, where to log errors to
+
+        Returns:
+        - Nothing
+          On success, this method returns; on error it raises an exception.
+
+        Raises:
+        - ManifestError is raised if the DTD file cannot be loaded,
+          or if validation fails.
+    '''
+
+    try:
+        dtd = etree.DTD(dtd_file)
+    except etree.DTDParseError, error:
+        msg = "Unable to parse DTD file [%s]:" % (dtd_file)
+        logger.exception(msg)
+        logger.exception(str(error))
+        raise ManifestError(msg, orig_exception=error)
+
+    if not dtd.validate(tree.getroot()):
+        msg = "Validation against DTD [%s] failed" % (dtd_file)
+        logger.error(msg)
+
+        for error in dtd.error_log.filter_from_errors():
+            logger.error(str(error))
+            msg = msg + " : " + str(error)
+
+        raise ManifestError(msg)
+
+
+__all__ = ["parser", "writer"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/parser/Makefile	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,63 @@
+#
+##
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+
+PYMODS		= parser.py
+
+PYCMODS		= $(PYMODS:%.py=%.pyc)
+
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORSOLINSTALLMANIFEST)/%)
+
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORSOLINSTALLMANIFEST)/%)
+
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+include ../../Makefile.lib
+
+static:
+
+dynamic:
+
+python:
+	$(PYTHON) -m compileall -l $(@D)
+
+all:		python
+
+install_h:
+
+install:	all .WAIT \
+		$(ROOTPYTHONVENDOR) \
+		$(ROOTPYTHONVENDORSOLINSTALL) \
+		$(ROOTPYTHONVENDORSOLINSTALLMANIFEST) \
+		$(ROOTPYMODS) $(ROOTPYCMODS)
+
+lint:		lint_SRCS
+
+FRC:
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/parser/parser.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,331 @@
+#
+##
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestParser Checkpoint'''
+
+import logging
+from lxml import etree
+import os
+
+from solaris_install.data_object import ParsingError
+from solaris_install.engine import InstallEngine
+from solaris_install.engine.checkpoint import AbstractCheckpoint
+from solaris_install.manifest import ManifestError, validate_manifest
+
+
+class ManifestParser(AbstractCheckpoint):
+    '''
+        ManifestParser - parse, validate and import an XML manifest.
+
+
+        Summary:
+        This class implements the AbstractCheckpoint abstract base class
+        which allows it to be executed from within the InstallEngine.
+        It also provides an additional API that allow it to be run
+        outside of the InstallEngine context.
+
+
+        Initializer method:
+        The parameters of the initializer method give the location
+        of the XML manifest to be parsed and define what validation and
+        other optional operations will be performed.
+
+
+        execute() and parse() methods:
+        When running in an InstallEngine context the execute() method
+        performs the main tasks of parsing and, optionally, validating
+        the manifest.
+
+        When run outside the InstallEngine context, the parse() method
+        performs these same functions.
+
+        The main difference between execute() and parse() is whether or
+        not the manifest data is imported into a DataObjectCache (DOC)
+        instance and from where the reference to the DOC is obtained.
+        execute() assumes an InstallEngine singleton exists and gets
+        the DOC reference from it.  With parse(), importing to the DOC
+        is optional and if required, a reference to an existing DOC
+        instance must be passed in.
+
+        If an error occurs during the execute() or parse() methods,
+        including XML syntax errors or failure to validate the
+        manifest, a ManifestError exception is raised.
+
+        If no errors occur, the methods simply return, with no return
+        value.  If importing to a DOC has been requested, the data
+        will be available in the DOC for retrieval by other system
+        components.
+
+
+        Other public methods:
+        The get_progress_estimate() method is required by the parent
+        AbstractCheckpoint class.
+
+        The cancel() method defined in AbstractCheckpoint is not
+        overridden in ManifestParser.
+    '''
+
+    def __init__(self, name, manifest, validate_from_docinfo=None,
+        dtd_file=None, load_defaults=True, call_xinclude=False):
+        '''
+            Class initializer method.
+
+            Parameters:
+            - name arg is required by AbstractCheckpoint.  Not used.
+            - manifest must be the path to a readable XML file
+            - validate_from_docinfo controls whether the manifest is
+              validated against the DTD in the file's XML headers, if any.
+              The default value is None.  This parameter can have 3 values:
+              None: validate against the DTD, if present; if DTD URI is not
+                present, no error is raised
+              True: attempt to validate against the DTD on-the-fly as it is
+                loaded.  If DTD is not specified, raise an error
+              False: whether or not a DTD is specified, do not attempt to
+                validate against it
+              If validation is attempted and fails, an error is raised.
+            - dtd_file specifes the path to a DTD against which the manifest
+              will be validated.  This validation may be performed instead
+              of, or as well as, the validation controlled by
+              validate_from_docinfo, or it may be skipped by leaving dtd_file
+              as None (the default).  If validation is attempted and fails,
+              an error is raised.
+              Note: default attribute values (see below) cannot be loaded
+              during this form of validation - they can only be loaded if
+              the manifest directly references a DTD in its headers.
+            - load_defaults must be either True or False.  The default value
+              is True.  load_defaults is only relevant when the manifest
+              references a DTD.  If True, default attribute values from the
+              DTD are loaded when the manifest is parsed.  If the manifest
+              does not reference a DTD, no defaults are loaded and no error
+              is raised.  (Note: Defaults can be loaded even if
+              validate_from_docinfo is False.)
+            - call_xinclude must be either True or False and controls
+              whether XInclude statements in the manifest will be processed
+              or not.  It defaults to False
+              Note: Currently, the on-the-fly validation performed if
+              validate_from_docinfo=True occurs *before* XInclude statements
+              are processed and validation triggered when
+              validate_from_docinfo=None or when dtd_file is specified occurs
+              *after* XInclude processing.  XInclude processing may affect
+              whether validation succeeds or not, so this ordering may need
+              to be considered.
+
+            Returns:
+            - Nothing
+
+            Raises:
+            - ManifestError is raised if invalid values are specified
+              for any paramaters or if manifest and/or dtd_file are
+              specified but are not actual files
+        '''
+
+        super(ManifestParser, self).__init__(name)
+
+        self.logger.debug("Initializing ManifestParser " \
+            "(manifest=%s, validate_from_docinfo=%s, dtd_file=%s, " \
+            "load_defaults=%s, call_xinclude=%s)",
+            manifest, validate_from_docinfo, dtd_file,
+            load_defaults, call_xinclude)
+
+        # Check params
+
+        if manifest is None:
+            raise ManifestError("No manifest specified")
+        if not os.path.isfile(manifest):
+            raise ManifestError("Manifest [%s] is not a file" % manifest)
+        self._manifest = manifest
+
+        self._validate_from_docinfo = validate_from_docinfo
+
+        if ((dtd_file is not None) and
+            (not os.path.isfile(dtd_file))):
+            raise ManifestError("DTD [%s] is not a file" % dtd_file)
+        self._dtd_file = dtd_file
+
+        self._load_defaults = load_defaults
+
+        self._call_xinclude = call_xinclude
+
+
+    def get_progress_estimate(self):
+        '''
+            The parent class requires that this method be implemented
+            in sub-classes.
+
+            This returns an estimate of how long the execute() method
+            will take to run.
+        '''
+
+        return 1
+
+
+    def parse(self, doc=None):
+        '''
+            This API method is not part of the AbstractCheckpoint spec.
+            It can be used to access the ManifestParser functionality outside
+            the InstallEngine context.
+
+            This method is also used as a convenience function within this
+            class to do most of the work of the execute() method.
+
+            Parameters:
+            - doc, a reference to the DataObjectCache in which to store
+              the manifest data.  If None, the manifest data will not be
+              stored anywhere, in which case this method only serves to
+              confirm whether the manifest can be parsed and, optionally,
+              validated.
+
+            Returns:
+            - Nothing
+              On success, this method returns; on error it raises an exception.
+
+            Raises:
+            - ManifestError is raised if an error occurs in _load_manifest()
+              or validate_manifest() or if
+              DataObjectCache.import_from_manifest_xml() raises a ParsingError
+              exception.
+        '''
+
+        self.logger.debug("ManifestParser.parse(doc=%s) called", doc)
+
+        if self._cancel_requested.is_set():
+            self.logger.debug("Cancel requested, returning.")
+            return
+
+        self.logger.debug("loading manifest (dtd_validation=%s)",
+            self._validate_from_docinfo)
+        tree = self._load_manifest(dtd_validation=self._validate_from_docinfo,
+            attribute_defaults=self._load_defaults)
+
+        if self._cancel_requested.is_set():
+            self.logger.debug("Cancel requested, returning.")
+            return
+
+        if self._validate_from_docinfo is None:
+            if ((tree.docinfo is not None) and
+                (tree.docinfo.system_url is not None)):
+                validate_manifest(tree, tree.docinfo.system_url, self.logger)
+
+        if self._dtd_file is not None:
+            validate_manifest(tree, self._dtd_file, self.logger)
+
+        if self._cancel_requested.is_set():
+            self.logger.debug("Cancel requested, returning.")
+            return
+
+        if doc is not None:
+            # import the Manifest data into the Volatile sub-tree
+            # of the DataObjectCache
+
+            try:
+                doc.import_from_manifest_xml(tree.getroot(), volatile=True)
+            except ParsingError, error:
+                msg = "Unable to import manifest into data_object_cache"
+                self.logger.exception(msg)
+                self.logger.exception(error)
+                raise ManifestError(msg, orig_exception=error)
+
+
+    def execute(self, dry_run=False):
+        '''
+            Abstract method defined in AbstractCheckpoint class.
+
+            Loads the specified Manifest and does the requested validation.
+            Imports resulting data into DOC.
+
+            Parameters:
+            - the dry_run keyword paramater, specified in AbstractCheckpoint,
+              is ignored in this method.
+
+            Returns:
+            - Nothing
+              On success, this method returns; on error it raises an exception.
+
+            Raises:
+            - ManifestError is raised if unable to fetch DOC reference or
+              if an error occurs in parse().
+        '''
+
+        self.logger.debug("ManifestParser.execute(dry_run=%s) called", dry_run)
+
+        engine = InstallEngine.get_instance()
+
+        doc = engine.data_object_cache
+        if doc is None:
+            raise ManifestError("Cannot get DOC reference from InstallEngine")
+
+        self.parse(doc=doc)
+
+
+    def _load_manifest(self, dtd_validation=False, attribute_defaults=True):
+        '''
+            Loads the manifest contained in self._manifest.
+
+            Parameters:
+            - dtd_validation must be True or False.  Default is False.  If
+              True, then the document will also be validated on-the-fly as
+              it is loaded.
+            - attribute_defaults must be True or False.  Default is True.
+              Only relevant if the manifest references a DTD.  If True, then
+              default values for XML attributes given in the DTD will be
+              loaded as the document is parsed.
+
+            Returns:
+            - an etree.ElementTree object
+
+            Raises:
+            - ManifestError is raised if the manifest file cannot be
+              accessed or if XMLSyntaxError is raised while parsing
+              and, optionally, validating it.
+        '''
+
+        # Create the XML parser to be used when processing the manifest.
+        parser = etree.XMLParser(remove_blank_text=True,
+            dtd_validation=dtd_validation,
+            attribute_defaults=attribute_defaults)
+
+        try:
+            tree = etree.parse(self._manifest, parser)
+        except IOError, error:
+            msg = "Cannot access Manifest file [%s]" % (self._manifest)
+            self.logger.exception(msg)
+            self.logger.exception(error)
+            raise ManifestError(msg, orig_exception=error)
+        except etree.XMLSyntaxError, error:
+            msg = "XML syntax error in manifest [%s]" % \
+                (self._manifest)
+            self.logger.exception(msg)
+            self.logger.exception(error)
+            raise ManifestError(msg, orig_exception=error)
+
+        if self._call_xinclude:
+            tree.xinclude()
+
+        if self.logger.isEnabledFor(logging.DEBUG):
+            self.logger.debug("Parsed XML document:\n%s",
+                etree.tostring(tree, pretty_print=True, method="xml"))
+
+        return tree
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/common.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,87 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestParser/ManifestWriter test common module'''
+
+import os
+
+TEST_BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+
+MANIFEST_DC = "%s/manifests/manifest_simple.xml" % TEST_BASE_DIR
+MANIFEST_DC_DTD_PATH = "%s/manifests/manifest_simple_dtd_path.xml" % TEST_BASE_DIR
+MANIFEST_NO_DTD_REF = "%s/manifests/manifest_no_dtd_ref.xml" % TEST_BASE_DIR
+MANIFEST_XINCLUDE = "%s/manifests/manifest_xinclude_main.xml" % TEST_BASE_DIR
+MANIFEST_INVALID = "%s/manifests/manifest_invalid.xml" % TEST_BASE_DIR
+MANIFEST_PARSE_ERROR = "%s/manifests/manifest_parse_error.xml" % TEST_BASE_DIR
+MANIFEST_SYNTAX_ERROR = "%s/manifests/manifest_syntax_error.xml" % TEST_BASE_DIR
+MANIFEST_NON_EXISTENT = "non/existent/dir/manifest.dtd"
+
+MANIFEST_OUT_OK = "/tmp/test_manifest_writer_01.xml"
+MANIFEST_OUT_NON_EXISTENT = "non/existent/dir/test_manifest_writer_01.xml"
+
+DTD_DC = "%s/manifests/manifest.dtd" % TEST_BASE_DIR
+DTD_INVALID = "/etc/release"
+DTD_INVALID_2 = "/tmp"
+DTD_NON_EXISTENT = "non/existent/dir/manifest.dtd"
+
+XSLT_DOC_TO_DC = "%s/manifests/doc-to-manifest.xslt" % TEST_BASE_DIR
+XSLT_INVALID = "/etc/release"
+XSLT_NON_EXISTENT = "non/existent/dir/file.xslt"
+
+
+def file_line_matches(filename, lineno, string):
+    '''
+        Returns True if line number 'lineno' of file 'filename'
+        matches the string 'string'.  Returns False if file
+        cannot be read, does not contain enough lines or if
+        the text doesn't match.
+
+        lineno is the line number within the file, starting at 0.
+        If lineno is a negative number it is taken to indicate a
+        number of lines from the end of the file, where -1 indicates
+        the last line of the file, -2 indicates the second last
+        line, etc.
+
+        Examples:
+        file_line_matches(filename, 0, string)  # match first line of file
+        file_line_matches(filename, -1, string) # match last line of file
+
+    '''
+
+    try:
+        file_obj = open(filename)
+        # read in entire file
+        lines = file_obj.readlines()
+    except IOError:
+        return False
+    finally:
+        file_obj.close()
+
+    try:
+        line = lines[lineno].strip()
+    except IndexError:
+        return False
+
+    return (line == string) 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/configuration.dtd	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,44 @@
+<!--
+ 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) 2010, Oracle and/or its affiliates. All rights reserved.
+
+-->
+
+<!ELEMENT configuration (validation?)>
+<!ATTLIST configuration source CDATA #REQUIRED>
+<!ATTLIST configuration dest CDATA #IMPLIED>
+
+<!--
+	Default to user configuration if type is not set. 
+-->
+<!ATTLIST configuration type (network|sysconf|zone|user) #IMPLIED>
+
+<!--
+	Configuration name should match the name of the checkpoint consuming
+	the configuration data.
+-->
+<!ATTLIST configuration name CDATA #REQUIRED>
+
+<!ELEMENT validation EMPTY>
+<!ATTLIST validation path CDATA #IMPLIED>
+<!ATTLIST validation args CDATA #IMPLIED>
+<!ATTLIST validation on_error CDATA "stop">
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/doc-to-manifest.xslt	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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) 2010, Oracle and/or its affiliates. All rights reserved.
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+    <xsl:output method="xml" indent="yes" encoding="UTF-8" doctype-system="file:///tmp/manifest.dtd"/>
+
+    <!-- Restructure the contents of the DataObjectCache to -->
+    <!-- resemble a DC Manifest. -->
+    <!-- For now, we are only doing a high-level restructuring. -->
+    <!-- That is, we create the top-level <dc> element, the -->
+    <!-- second-level <distro> element (with any relevant attributes -->
+    <!-- from the DOC) and the 5 sub-elements of <distro>: -->
+    <!-- <distro_spec>, <target>, <software>, <execution> and -->
+    <!-- <configuration>, copied unmodified and in their -->
+    <!-- entirety from the DOC, as many times as they occur. -->
+
+    <xsl:template match="/">
+        <dc>
+            <distro>
+                <xsl:choose>
+                    <xsl:when test="//distro">
+                        <!-- Copy all <distro>'s attributes. -->
+                        <xsl:copy-of select="//distro/@*"/>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <!-- If there was no <distro> in source document, -->
+                        <!-- then provide the compulsory name attribute. -->
+                        <xsl:attribute name="name">
+                            <xsl:text>generated manifest</xsl:text>
+                        </xsl:attribute>
+                    </xsl:otherwise>
+                </xsl:choose>
+
+                <!-- Place these other elements (and their sub-elements) -->
+                <!-- directly under distro, regardless of where they -->
+                <!-- were found in the source document. If they are not -->
+                <!-- found, they will just be skipped.  -->
+                <xsl:copy-of select="//distro_spec"/>
+                <xsl:copy-of select="//target"/>
+                <xsl:copy-of select="//software"/>
+                <xsl:copy-of select="//execution"/>
+                <xsl:copy-of select="//configuration"/>
+            </distro>
+        </dc>
+    </xsl:template>
+</xsl:stylesheet>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/execution.dtd	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,34 @@
+<!ELEMENT execution (checkpoint+)>
+
+<!-- 
+	The stop_on_error attribute is for the whole execution sequence. 
+	Not per checkpoint. 
+-->
+<!ATTLIST execution stop_on_error (true|false) "true">
+
+<!--
+	The name of a checkpoint is a unique to the registration instance.
+	The mod_path is relative to the PYTHONPATH. The checkpoint_class
+	is required so we can associate a checkpoint class with the
+	mod_path which is a file that contains the checkpoint class. There
+	may not necessarily be a 1-1 relationship.
+-->
+<!ELEMENT checkpoint (args?, kwargs?)>
+<!ATTLIST checkpoint name CDATA #REQUIRED>
+<!ATTLIST checkpoint desc CDATA #IMPLIED>
+<!ATTLIST checkpoint mod_path CDATA #REQUIRED>
+<!ATTLIST checkpoint log_level CDATA #IMPLIED>
+<!ATTLIST checkpoint checkpoint_class CDATA #REQUIRED>
+
+<!--
+	Args are non keyword arguments, which can be parsed by the parser
+	if required. kwargs are keyword arguments which must conform to
+	the published format. Each checkpoint will provide the format
+	if kwargs are enabled.
+-->
+
+<!ELEMENT args (#PCDATA)>
+<!ELEMENT kwargs (arg+)>
+
+<!ELEMENT arg (#PCDATA)>
+<!ATTLIST arg name CDATA #REQUIRED>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest.dtd	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,53 @@
+<!ELEMENT dc (distro)>
+
+<!ENTITY % target SYSTEM "target.dtd">
+%target;
+
+<!ENTITY % execution SYSTEM "execution.dtd">
+%execution;
+
+<!ENTITY % configuration SYSTEM "configuration.dtd">
+%configuration;
+
+<!ENTITY % software SYSTEM "software.dtd">
+%software;
+
+<!--
+	If no special distro args, then we don't have to have a
+	distro_spec element. We do require target, execution. Software
+	and configuration are optional, although without software
+	we cannot create a media image. The optional software elements
+	are due to the VM image not requiring a software specification.
+	Compression settings are specified as an argument to the 
+	associated execution checkpoint element.
+-->
+
+<!ELEMENT distro (distro_spec?, target+, software*, execution+, configuration*)>
+<!ATTLIST distro name CDATA #REQUIRED>
+<!ATTLIST distro incremental_media_name (true|false) "false">
+<!ATTLIST distro http_proxy CDATA #IMPLIED>
+
+<!ELEMENT distro_spec (img_params*)>
+
+<!ELEMENT img_params (media_im|vm_im)>
+
+<!ELEMENT media_im (grub_mods*, max_size?)>
+<!ELEMENT grub_mods (grub_entry*)>
+<!ATTLIST grub_mods min_mem CDATA #IMPLIED>
+<!ATTLIST grub_mods title CDATA #IMPLIED>
+<!ATTLIST grub_mods default_entry CDATA #IMPLIED>
+<!ATTLIST grub_mods timeout CDATA #IMPLIED>
+
+<!ELEMENT grub_entry (title_suffix, line+)>
+<!ATTLIST grub_entry position CDATA #IMPLIED>
+<!ELEMENT title_suffix (#PCDATA)>
+<!ELEMENT line (#PCDATA)>
+
+<!--
+	max_size refers to the maximum size an image area can get,
+	if the user wants to define this.
+-->
+
+<!ELEMENT max_size (size)>
+
+<!ELEMENT vm_im EMPTY>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest_invalid.xml	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE dc SYSTEM "manifest.dtd">
+<dc>
+<distro name="OpenSolaris_X86.iso"
+	http_proxy="http://example.com">
+<distro_spec>
+    <img_params>
+        <media_im>
+			<grub_mods min_mem="0" title="myentry" default_entry="0" 
+				timeout="5">
+				<grub_entry position="1">
+					<title_suffix>Enable SSH</title_suffix>
+					<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B
+							livessh=enable</line>
+					<line>module$ /platform/i86pc/$ISADIR/boot_archive</line>
+				</grub_entry>
+			</grub_mods>
+		</media_im>
+	</img_params>
+</distro_spec>	
+
+<!--
+	Software section.
+-->
+<software name="transfer-ips-install">
+    <destination>
+        <image img_root="/rpool/new_dc_test/build_data/pkg_image" action="create"></image>
+    </destination>
+    <source>
+        <publisher name="opensolaris.org">
+            <origin name="http://ipkg.sfbay/dev"></origin>
+        </publisher>
+    </source>
+    <software_data action="install" type="IPS">
+        <name>SUNWcs</name>
+        <name>SUNWcsd</name>
+        <name>entire</name>
+    </software_data>
+    <software_data action="install" type="IPS">
+        <name>slim_install</name>
+        <name>system/install/media/internal</name>
+    </software_data>
+</software>
+<!--
+	Execution section.
+-->
+<execution stop_on_error="true">
+	<checkpoint name="transfer-ips-install"
+		mod_path="solaris_install/transfer/transfer_ips"
+	    checkpoint_class="TransferIPS">
+        <kwargs>
+            <arg name="dist_iso_sort">path</arg>
+        </kwargs>
+    </checkpoint>
+</execution>
+
+<!--
+	Image SMF configuration. Applied to boot archive in the order they
+	are specified. Image files are always used.
+-->
+
+</distro>
+</dc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest_no_dtd_ref.xml	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dc>
+<distro name="OpenSolaris_X86.iso"
+	http_proxy="http://example.com">
+<distro_spec>
+    <img_params>
+        <media_im>
+			<grub_mods min_mem="0" title="myentry" default_entry="0" 
+				timeout="5">
+				<grub_entry position="1">
+					<title_suffix>Enable SSH</title_suffix>
+					<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B
+							livessh=enable</line>
+					<line>module$ /platform/i86pc/$ISADIR/boot_archive</line>
+				</grub_entry>
+			</grub_mods>
+		</media_im>
+	</img_params>
+</distro_spec>	
+
+<target>
+    <target_device>
+        <swap>
+            <zvol name="swap">
+                <size val="0"/>
+            </zvol>
+        </swap>
+    </target_device>
+</target>
+
+<!--
+	Software section.
+-->
+<software name="transfer-ips-install">
+    <destination>
+        <image img_root="/rpool/new_dc_test/build_data/pkg_image" action="create"></image>
+    </destination>
+    <source>
+        <publisher name="opensolaris.org">
+            <origin name="http://ipkg.sfbay/dev"></origin>
+        </publisher>
+    </source>
+    <software_data action="install" type="IPS">
+        <name>SUNWcs</name>
+        <name>SUNWcsd</name>
+        <name>entire</name>
+    </software_data>
+    <software_data action="install" type="IPS">
+        <name>slim_install</name>
+        <name>system/install/media/internal</name>
+    </software_data>
+</software>
+<!--
+	Execution section.
+-->
+<execution stop_on_error="true">
+	<checkpoint name="transfer-ips-install"
+		mod_path="solaris_install/transfer/transfer_ips"
+	    checkpoint_class="TransferIPS">
+        <kwargs>
+            <arg name="dist_iso_sort">path</arg>
+        </kwargs>
+    </checkpoint>
+</execution>
+
+<!--
+	Image SMF configuration. Applied to boot archive in the order they
+	are specified. Image files are always used.
+-->
+
+</distro>
+</dc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest_parse_error.xml	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE dc SYSTEM "manifest.dtd">
+<dc>
+<distro name="OpenSolaris_X86.iso"
+	http_proxy="http://example.com">
+<distro_spec>
+    <img_params>
+        <media_im>
+			<grub_mods min_mem="0" title="myentry" default_entry="0" 
+				timeout="5">
+				<grub_entry position="1">
+					<title_suffix>Enable SSH</title_suffix>
+					<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B
+							livessh=enable</line>
+					<line>module$ /platform/i86pc/$ISADIR/boot_archive</line>
+				</grub_entry>
+			</grub_mods>
+		</media_im>
+	</img_params>
+</distro_spec>	
+
+<target>
+    <target_device>
+        <swap>
+            <zvol name="swap">
+                <size val="0"/>
+            </zvol>
+        </swap>
+    </target_device>
+</target>
+
+<!--
+	Software section.
+-->
+<software name="transfer-ips-install">
+    <destination>
+        <image img_root="/rpool/new_dc_test/build_data/pkg_image" action="create"></image>
+    </destination>
+    <source>
+        <publisher name="opensolaris.org">
+            <origin name="http://ipkg.sfbay/dev"></origin>
+        </publisher>
+    </source>
+    <software_data action="install" type="IPS">
+        <name>SUNWcs</name>
+        <name>SUNWcsd</name>
+        <name>entire</name>
+    </software_data>
+    <software_data action="install" type="IPS">
+        <name>slim_install</name>
+        <name>system/install/media/internal</name>
+    </software_data>
+</software>
+<!--
+	Execution section.
+-->
+<execution stop_on_error="true">
+	<checkpoint name="transfer-ips-install"
+		mod_path="solaris_install/transfer/transfer_ips"
+	    checkpoint_class="TransferIPS">
+        <kwargs>
+            <argX name="dist_iso_sort">path</argX>
+        </kwargs>
+    </checkpoint>
+</execution>
+
+<!--
+	Image SMF configuration. Applied to boot archive in the order they
+	are specified. Image files are always used.
+-->
+
+</distro>
+</dc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest_simple.xml	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE dc SYSTEM "manifest.dtd">
+<dc>
+<distro name="OpenSolaris_X86.iso"
+	http_proxy="http://example.com">
+<distro_spec>
+    <img_params>
+        <media_im>
+			<grub_mods min_mem="0" title="myentry" default_entry="0" 
+				timeout="5">
+				<grub_entry position="1">
+					<title_suffix>Enable SSH</title_suffix>
+					<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B
+							livessh=enable</line>
+					<line>module$ /platform/i86pc/$ISADIR/boot_archive</line>
+				</grub_entry>
+			</grub_mods>
+		</media_im>
+	</img_params>
+</distro_spec>	
+
+<target>
+    <target_device>
+        <swap>
+            <zvol name="swap">
+                <size val="0"/>
+            </zvol>
+        </swap>
+    </target_device>
+</target>
+
+<!--
+	Software section.
+-->
+<software name="transfer-ips-install">
+    <destination>
+        <image img_root="/rpool/new_dc_test/build_data/pkg_image" action="create"></image>
+    </destination>
+    <source>
+        <publisher name="opensolaris.org">
+            <origin name="http://ipkg.sfbay/dev"></origin>
+        </publisher>
+    </source>
+    <software_data action="install" type="IPS">
+        <name>SUNWcs</name>
+        <name>SUNWcsd</name>
+        <name>entire</name>
+    </software_data>
+    <software_data action="install" type="IPS">
+        <name>slim_install</name>
+        <name>system/install/media/internal</name>
+    </software_data>
+</software>
+<!--
+	Execution section.
+-->
+<execution stop_on_error="true">
+	<checkpoint name="transfer-ips-install"
+		mod_path="solaris_install/transfer/transfer_ips"
+	    checkpoint_class="TransferIPS">
+        <kwargs>
+            <arg name="dist_iso_sort">path</arg>
+        </kwargs>
+    </checkpoint>
+</execution>
+
+<!--
+	Image SMF configuration. Applied to boot archive in the order they
+	are specified. Image files are always used.
+-->
+
+</distro>
+</dc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest_simple_dtd_path.xml	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE dc SYSTEM "lib/install_manifest/test/manifests/manifest.dtd">
+<dc>
+<distro name="OpenSolaris_X86.iso"
+	http_proxy="http://example.com">
+<distro_spec>
+    <img_params>
+        <media_im>
+			<grub_mods min_mem="0" title="myentry" default_entry="0" 
+				timeout="5">
+				<grub_entry position="1">
+					<title_suffix>Enable SSH</title_suffix>
+					<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B
+							livessh=enable</line>
+					<line>module$ /platform/i86pc/$ISADIR/boot_archive</line>
+				</grub_entry>
+			</grub_mods>
+		</media_im>
+	</img_params>
+</distro_spec>	
+
+<target>
+    <target_device>
+        <swap>
+            <zvol name="swap">
+                <size val="0"/>
+            </zvol>
+        </swap>
+    </target_device>
+</target>
+
+<!--
+	Software section.
+-->
+<software name="transfer-ips-install">
+    <destination>
+        <image img_root="/rpool/new_dc_test/build_data/pkg_image" action="create"></image>
+    </destination>
+    <source>
+        <publisher name="opensolaris.org">
+            <origin name="http://ipkg.sfbay/dev"></origin>
+        </publisher>
+    </source>
+    <software_data action="install" type="IPS">
+        <name>SUNWcs</name>
+        <name>SUNWcsd</name>
+        <name>entire</name>
+    </software_data>
+    <software_data action="install" type="IPS">
+        <name>slim_install</name>
+        <name>system/install/media/internal</name>
+    </software_data>
+</software>
+<!--
+	Execution section.
+-->
+<execution stop_on_error="true">
+	<checkpoint name="transfer-ips-install"
+		mod_path="solaris_install/transfer/transfer_ips"
+	    checkpoint_class="TransferIPS">
+        <kwargs>
+            <arg name="dist_iso_sort">path</arg>
+        </kwargs>
+    </checkpoint>
+</execution>
+
+<!--
+	Image SMF configuration. Applied to boot archive in the order they
+	are specified. Image files are always used.
+-->
+
+</distro>
+</dc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest_syntax_error.xml	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE dc SYSTEM "manifest.dtd">
+<dc>
+distro name="OpenSolaris_X86.iso"
+	http_proxy="http://example.com">
+<distro_spec>
+    <img_params>
+        <media_im>
+			<grub_mods min_mem="0" title="myentry" default_entry="0" 
+				timeout="5">
+				<grub_entry position="1">
+					<title_suffix>Enable SSH</title_suffix>
+					<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B
+							livessh=enable</line>
+					<line>module$ /platform/i86pc/$ISADIR/boot_archive</line>
+				</grub_entry>
+			</grub_mods>
+		</media_im>
+	</img_params>
+</distro_spec>	
+
+<target>
+    <target_device>
+        <swap>
+            <zvol name="swap">
+                <size val="0"/>
+            </zvol>
+        </swap>
+    </target_device>
+</target>
+
+<!--
+	Software section.
+-->
+<software name="transfer-ips-install">
+    <destination>
+        <image img_root="/rpool/new_dc_test/build_data/pkg_image" action="create"></image>
+    </destination>
+    <source>
+        <publisher name="opensolaris.org">
+            <origin name="http://ipkg.sfbay/dev"></origin>
+        </publisher>
+    </source>
+    <software_data action="install" type="IPS">
+        <name>SUNWcs</name>
+        <name>SUNWcsd</name>
+        <name>entire</name>
+    </software_data>
+    <software_data action="install" type="IPS">
+        <name>slim_install</name>
+        <name>system/install/media/internal</name>
+    </software_data>
+</software>
+<!--
+	Execution section.
+-->
+<execution stop_on_error="true">
+	<checkpoint name="transfer-ips-install"
+		mod_path="solaris_install/transfer/transfer_ips"
+	    checkpoint_class="TransferIPS">
+        <kwargs>
+            <arg name="dist_iso_sort">path</arg>
+        </kwargs>
+    </checkpoint>
+</execution>
+
+<!--
+	Image SMF configuration. Applied to boot archive in the order they
+	are specified. Image files are always used.
+-->
+
+</distro>
+</dc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest_xinclude_main.xml	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE dc SYSTEM "manifest.dtd">
+<dc>
+<distro name="OpenSolaris_X86.iso"
+	http_proxy="http://example.com">
+<distro_spec>
+    <img_params>
+        <media_im>
+			<grub_mods min_mem="0" title="myentry" default_entry="0" 
+				timeout="5">
+				<grub_entry position="1">
+					<title_suffix>Enable SSH</title_suffix>
+					<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B
+							livessh=enable</line>
+					<line>module$ /platform/i86pc/$ISADIR/boot_archive</line>
+				</grub_entry>
+			</grub_mods>
+		</media_im>
+	</img_params>
+</distro_spec>	
+
+<include href="manifest_xinclude_target.xml" xmlns="http://www.w3.org/2001/XInclude"/>
+
+<!--
+	Software section.
+-->
+<software name="transfer-ips-install">
+    <destination>
+        <image img_root="/rpool/new_dc_test/build_data/pkg_image" action="create"></image>
+    </destination>
+    <source>
+        <publisher name="opensolaris.org">
+            <origin name="http://ipkg.sfbay/dev"></origin>
+        </publisher>
+    </source>
+    <software_data action="install" type="IPS">
+        <name>SUNWcs</name>
+        <name>SUNWcsd</name>
+        <name>entire</name>
+    </software_data>
+    <software_data action="install" type="IPS">
+        <name>slim_install</name>
+        <name>system/install/media/internal</name>
+    </software_data>
+</software>
+<!--
+	Execution section.
+-->
+<execution stop_on_error="true">
+	<checkpoint name="transfer-ips-install"
+		mod_path="solaris_install/transfer/transfer_ips"
+	    checkpoint_class="TransferIPS">
+        <kwargs>
+            <arg name="dist_iso_sort">path</arg>
+        </kwargs>
+    </checkpoint>
+</execution>
+
+<!--
+	Image SMF configuration. Applied to boot archive in the order they
+	are specified. Image files are always used.
+-->
+
+</distro>
+</dc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/manifest_xinclude_target.xml	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,11 @@
+
+<target>
+    <target_device>
+        <swap>
+            <zvol name="swap">
+                <size val="0"/>
+            </zvol>
+        </swap>
+    </target_device>
+</target>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/software.dtd	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,107 @@
+<!--
+ 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) 2010, Oracle and/or its affiliates. All rights reserved.
+
+-->
+
+<!ELEMENT software (destination?, source*, software_data*)>
+
+<!--
+	The software name is utilized to allow users to associate
+	a specific software instance with a specific checkpoint.
+	For example, we could have multiple transfer types during the
+	course of an installation process. If a software is
+	to be associated with a specific checkpoint the software inst
+	name must be utilized and must be the same as the associated
+	checkpoint. If no name provided the software elements
+	will be used in order based on the type provided.
+-->
+
+<!ATTLIST software name CDATA #IMPLIED>
+
+<!ELEMENT software_data (name*)>
+<!ATTLIST software_data action (install|uninstall|unpack|noinstall) "install">
+<!ATTLIST software_data type (IPS|SVR4|ARCHIVE|IMAGE|P5I|DU|P5P|FILE|DIR) "IPS">
+
+<!ELEMENT name (#PCDATA)>
+
+<!--
+	Destination element is not required. If specified there can only
+	be one destination per software element. If not specified,
+	the destination is assumed to be an ipkg image and will be
+	discovered automatically. 
+-->
+
+<!ELEMENT destination (image|dir)>
+
+<!ELEMENT image (facet*, img_type?, property*)>
+<!ATTLIST image action (create|use_existing) "create">
+<!ATTLIST image index (true|false) "false">
+<!ATTLIST image ssl_key CDATA #IMPLIED>
+<!ATTLIST image ssl_cert CDATA #IMPLIED>
+<!ATTLIST image img_root CDATA #IMPLIED>
+
+<!ELEMENT img_type EMPTY>
+<!ATTLIST img_type completeness (full|partial) #REQUIRED>
+<!ATTLIST img_type zone (true|false) "false">
+
+<!--
+	A property on an image is set via pkg set-property <propname>.
+	So, for use in this schema an example would be:
+		<image>
+		    <img_type completeness="partial" zone="true"/>
+		    <property val="true">send-uuid</property>
+		    <property val="false">flush-content-cache-on-success
+			</property>  
+		</image>
+	A property can also require a string value, rather than just true
+	or false. 
+-->
+<!ELEMENT property (#PCDATA)>
+<!ATTLIST property val CDATA #REQUIRED>
+
+<!--
+	A facet is an option that may be selected or not selected,
+	such as various locales, documentation, etc. This is per
+	image.
+-->
+
+<!ELEMENT facet (#PCDATA)>
+<!ATTLIST facet set (true|false) "true">
+
+<!ELEMENT source (publisher+|dir)>
+
+<!--
+	If name is not specified, and this is an ips install,
+	then publishers known by the specified repository will be added to 
+	the image. Origin can be an uri, path to a file, archive, directory.
+-->
+<!ELEMENT publisher (origin+, mirror*)>
+<!ATTLIST publisher name CDATA #IMPLIED>
+
+<!ELEMENT origin EMPTY>
+<!ATTLIST origin name CDATA #REQUIRED>
+
+<!ELEMENT mirror EMPTY>
+<!ATTLIST mirror name CDATA #REQUIRED>
+
+<!ELEMENT dir EMPTY>
+<!ATTLIST dir path CDATA #REQUIRED>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/manifests/target.dtd	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,196 @@
+<!--
+ 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) 2010, Oracle and/or its affiliates. All rights reserved.
+
+-->
+
+<!ELEMENT target (target_device+)>
+
+<!--
+	A partition and slice element must be specified within a
+	containing element, such as a disk, zpool or vdev. There must
+	be one element, if disk or pool are specified, that is
+	tagged as the root device. If no target_devices are specified
+	the the application must choose the device based on its
+	specific criteria and assume this is the root device.
+-->
+
+<!ELEMENT target_device (disk|zpool+|swap|dump)>
+
+<!--
+	If a disk is specified at the top level, that is not contained
+	within a zpool specification, this disk will be assumed
+	to be the root device. If a disk target is specified
+	at the top level, and then a zpool with the is_root attribute
+	set this is an error. The user can specify a specific
+	slice within the disk to be used as the root slice. If
+	no slice specified then root slice will be 0.
+-->
+
+<!ELEMENT disk ((disk_name|disk_prop|disk_keyword|iscsi), partition*, slice*)>
+<!--
+	Disk name can be one of ctd, volid, devpath or devid name.
+	Default is "ctd".
+-->
+<!ELEMENT disk_name EMPTY>
+<!ATTLIST disk_name name CDATA #REQUIRED>
+<!ATTLIST disk_name name_type (ctd|volid|devpath|devid) "ctd">
+
+<!ELEMENT disk_prop EMPTY>
+<!ATTLIST disk_prop dev_type CDATA #IMPLIED>
+<!ATTLIST disk_prop dev_vendor CDATA #IMPLIED>
+<!ATTLIST disk_prop dev_size CDATA #IMPLIED>
+
+<!ELEMENT disk_keyword EMPTY>
+<!ATTLIST disk_keyword key (boot_disk) #REQUIRED>
+
+<!--
+	A vdev must start with a disk element. The slice and partition
+	elements use numerical names, such as 0 or 1. A disk must
+	be named for a vdev, using the disk element notation. 
+-->
+
+<!ELEMENT vdev (disk+, partition*, slice*)>
+<!ATTLIST vdev redundancy (mirror|raidz|raidz1|raidz2|raidz3|none) "mirror">
+
+<!ELEMENT dataset (zvol|filesystem)>
+
+<!--
+	No size specification means we create the slice the whole size of
+	the disk. If multiple slices specified for one disk, with
+	no sizes, this is an error. The attribute is_root is only
+	valid when a slice is part of a disk definition, outside of
+	a zpool definition. The user can request to format the disk
+	with multiple slices but specify one that they want to
+	be included in the root pool.
+
+-->
+
+
+<!ELEMENT slice (size?)>
+<!ATTLIST slice action (create|delete|preserve) "create">
+<!ATTLIST slice name CDATA #REQUIRED>
+<!ATTLIST slice is_root (true|false) #IMPLIED>
+
+<!--
+	The use of the 'force' attribute on slice specifies that on
+	a 'create' of a slice that already exists we overwrite the
+	slice if force==true. Otherwise the application errors.
+-->
+
+<!ATTLIST slice force (true|false) "false">
+
+<!--
+	If partition size is not provided the partition will be the
+	remaining free size left on the disk.
+-->
+
+<!ELEMENT partition (slice*, size?)>
+<!ATTLIST partition action (create|delete|use_existing) "create">
+
+<!--
+	A partition name is a numeric value, e.g. 1, will be
+	interpreted as partition 1. If a name is not provided 
+	the user must specify the use_existing action, otherwise
+	this will be an invalid specification.
+-->
+<!ATTLIST partition name CDATA #IMPLIED>
+<!ATTLIST partition part_type CDATA "191">
+
+<!--
+	Size must be suffixed with a size unit. i.e 100gb, 2secs, 2tb.
+-->
+<!ELEMENT size EMPTY>
+<!ATTLIST size val CDATA #REQUIRED>
+<!ATTLIST size start_sector CDATA #IMPLIED>
+
+
+<!ELEMENT options (#PCDATA)>
+
+<!--
+	Option elements allow any string type, and this string is parsable
+	character data, should the application require it.
+-->
+
+<!--
+	Filesystem options are for zfs filesystems. The format of these
+	is this: "-o property=value". Any editable ZFS filesystem property
+	can be set at creation time. Multiple -o options can be
+	specified. An error will occur if a propert is specified in
+	multiple -o options.
+-->
+
+<!ELEMENT filesystem (options?)>
+<!ATTLIST filesystem name CDATA #REQUIRED>
+<!ATTLIST filesystem action (create|delete|preserve) "create">
+<!ATTLIST filesystem mountpoint CDATA #IMPLIED>
+
+<!--
+	Redundancy needs to be part of the vdev grouping,
+	not a property on zpool itself. There can be multiple
+	vdev groupings within one pool configuration.
+-->
+
+<!ELEMENT zpool (vdev*, dataset*, pool_options?, dataset_options?)>
+<!ATTLIST zpool action (create|delete|preserve|use_existing) "create">
+<!ATTLIST zpool name CDATA #REQUIRED>
+<!ATTLIST zpool is_root (true|false) "false">
+
+<!--
+	The pool option string, which is also a parsable string, 
+	can include both pool options and filesystem options.
+	For pool options the format is: "-o property=value". For
+	filesystem properties the format is: "-O file-system-property=value"
+	Both of these typs of properties can be set in the option string.
+	An example of combining these in the option string:
+
+"-o altroot=/a -o autoexpand=off -o delegation=off -O atime=on -O compression=lzbj"
+-->
+
+<!ELEMENT pool_options (options)>
+<!ELEMENT dataset_options (options)>
+
+
+<!ELEMENT zvol (options?, size) >
+<!ATTLIST zvol action (create|delete|preserve|use_existing) "create">
+<!ATTLIST zvol name CDATA #REQUIRED>
+
+<!-- 
+	ISCSI does not have an action attribute. We use iscsi devices but
+	we do not operate directly on them.
+-->
+<!ELEMENT iscsi (ip)>
+<!ATTLIST iscsi name CDATA #REQUIRED>
+<!ATTLIST iscsi source CDATA #IMPLIED>
+<!ATTLIST iscsi target_lun CDATA #IMPLIED>
+<!ATTLIST iscsi target_port CDATA #IMPLIED>
+
+<!ELEMENT ip (#PCDATA)>
+
+<!--
+	Swap and dump are optional with Solaris install.
+-->
+
+<!ELEMENT swap (zvol)>
+<!ATTLIST swap no_swap (true|false) "false">
+
+<!ELEMENT dump (zvol)>
+<!ATTLIST dump no_dump (true|false) "false">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/test_mp_invalid_params.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,63 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestParser invalid params test module'''
+
+
+import logging
+import unittest
+
+import common
+from solaris_install.logger import InstallLogger
+from solaris_install.manifest import ManifestError
+from solaris_install.manifest.parser import ManifestParser
+
+
+class InvalidParams(unittest.TestCase):
+    '''ManifestParser invalid params tests'''
+
+    def setUp(self):
+        '''Set up logging'''
+        logging.setLoggerClass(InstallLogger)
+
+
+    def test_invalid_params_manifest(self):
+        '''
+            test_invalid_params_manifest - set manifest param to None
+        '''
+        self.assertRaises(ManifestError, ManifestParser, "manifest-parser",
+            None)
+
+
+    def test_invalid_params_dtd_file(self):
+        '''
+            test_invalid_params_dtd_file - set dtd_file param to directory name "/tmp"
+        '''
+        self.assertRaises(ManifestError, ManifestParser, "manifest-parser",
+            common.MANIFEST_DC, dtd_file=common.DTD_INVALID_2)
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/test_mp_load_manifest.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,173 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestParser tests for _load_manifest method'''
+
+
+import logging
+import unittest
+
+from lxml import etree
+
+import common
+from solaris_install.logger import InstallLogger
+from solaris_install.manifest import ManifestError
+from solaris_install.manifest.parser import ManifestParser
+
+
+class ManifestParserLoadManifest(unittest.TestCase):
+    '''ManifestParser tests for _load_manifest method'''
+
+    def setUp(self):
+        '''Set up logging'''
+        logging.setLoggerClass(InstallLogger)
+
+
+    def test_mp_load_manifest_valid(self):
+        '''
+            test_mp_load_manifest_valid - load and validate standard manifest
+        '''
+
+        validate_from_docinfo = True
+        load_defaults = False
+        call_xinclude = False
+        path = "/dc/distro"
+        attribute = "http_proxy"
+        attribute_value = "http://example.com"
+
+        try:
+            mp_cp = ManifestParser("manifest-parser",
+                common.MANIFEST_DC,
+                validate_from_docinfo=validate_from_docinfo,
+                load_defaults=load_defaults,
+                call_xinclude=call_xinclude)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        try:
+            tree = mp_cp._load_manifest(dtd_validation=validate_from_docinfo,
+                attribute_defaults=load_defaults)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        # confirm some expected value from the XML tree
+        elements = tree.xpath(path)
+        self.assertEqual(len(elements), 1)
+        self.assertTrue(elements[0].attrib.has_key(attribute))
+        self.assertEqual(elements[0].get(attribute), attribute_value)
+
+
+    def test_mp_load_manifest_defaults(self):
+        '''
+            test_mp_load_manifest_defaults - load a standard manifest with attribute defaults
+        '''
+
+        validate_from_docinfo = False
+        load_defaults = True
+        call_xinclude = False
+
+        try:
+            mp_cp = ManifestParser("manifest-parser",
+                common.MANIFEST_DC,
+                validate_from_docinfo=validate_from_docinfo,
+                load_defaults=load_defaults,
+                call_xinclude=call_xinclude)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        try:
+            tree = mp_cp._load_manifest(dtd_validation=validate_from_docinfo,
+                attribute_defaults=load_defaults)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        # confirm some expected value from the XML tree
+        # (this needs to wait until some more DataObject classes are written)
+
+
+    def test_mp_load_manifest_xinclude(self):
+        '''
+            test_mp_load_manifest_xinclude - load a manifest with XInclude statements
+        '''
+
+        validate_from_docinfo = False
+        load_defaults = False
+        call_xinclude = True
+        path = "/dc/distro/target/target_device/swap/zvol"
+        attribute = "name"
+        attribute_value = "swap"
+
+        try:
+            mp_cp = ManifestParser("manifest-parser",
+                common.MANIFEST_XINCLUDE,
+                validate_from_docinfo=validate_from_docinfo,
+                load_defaults=load_defaults,
+                call_xinclude=call_xinclude)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        try:
+            tree = mp_cp._load_manifest(dtd_validation=validate_from_docinfo,
+                attribute_defaults=load_defaults)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        # confirm some expected value from the XInclude'd portion of the XML tree
+        elements = tree.xpath(path)
+        self.assertEqual(len(elements), 1)
+        self.assertTrue(elements[0].attrib.has_key(attribute))
+        self.assertEqual(elements[0].get(attribute), attribute_value)
+
+
+    def test_mp_load_manifest_syntax(self):
+        '''
+            test_mp_load_manifest_syntax - ensure XMLSyntaxError raised for bad manifest
+        '''
+
+        validate_from_docinfo = False
+        load_defaults = False
+        call_xinclude = False
+
+        try:
+            mp_cp = ManifestParser("manifest-parser",
+                common.MANIFEST_SYNTAX_ERROR,
+                validate_from_docinfo=validate_from_docinfo,
+                load_defaults=load_defaults,
+                call_xinclude=call_xinclude)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        try:
+            tree = mp_cp._load_manifest(dtd_validation=validate_from_docinfo,
+                attribute_defaults=load_defaults)
+        except ManifestError, err:
+            # confirm the reason for faiing was an XMLSyntaxError
+            self.assertTrue(isinstance(err.orig_exception, etree.XMLSyntaxError))
+        else:
+            self.fail("_load_manifest should have failed.")
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/test_mp_with_engine.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,150 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestParser Tests using InstallEngine'''
+
+
+import logging
+import unittest
+
+import common
+from solaris_install.engine import InstallEngine
+from solaris_install.engine.test.engine_test_utils import reset_engine, \
+    get_new_engine_instance
+
+####################################################################
+# importing these classes causes them to be registered with the DOC
+####################################################################
+# pylint: disable-msg=W0614
+#from solaris_install.distro_const.configuration import *
+#from solaris_install.distro_const.distro_spec import *
+#from solaris_install.distro_const.execution_checkpoint import *
+from solaris_install.target.target_spec import *
+#from solaris_install.transfer.transfer_info import *
+
+
+class ManifestParserWithEngine(unittest.TestCase):
+    '''ManifestParser Tests using InstallEngine'''
+
+    def setUp(self):
+        '''instantiate the Engine so that the DOC is created'''
+        self.engine = get_new_engine_instance()
+
+    def tearDown(self):
+        '''Force all content of the DOC to be cleared.'''
+        reset_engine()
+
+
+    ### COMMENTED OUT UNTIL DC code is pushed
+    #def test_mp_engine_simple(self):
+    #    '''
+    #        test_mp_engine_simple - import and validate a standard manifest
+    #    '''
+    #    my_args = [common.MANIFEST_DC]
+    #    my_kwargs = {}
+    #    my_kwargs["validate_from_docinfo"] = True
+    #    my_kwargs["load_defaults"] = False
+
+    #    self.engine.register_checkpoint("manifest_parser",
+    #        "solaris_install/manifest/parser",
+    #        "ManifestParser",
+    #        args=my_args,
+    #        kwargs=my_kwargs)
+
+    #    status = self.engine.execute_checkpoints()[0]
+
+    #    self.assertEqual(status, InstallEngine.EXEC_SUCCESS,
+    #        "ManifestParser checkpoint failed [%s]" % status)
+
+    #    # Check some expected value from the cache
+    #    distro_list = \
+    #        self.engine.data_object_cache.get_descendants(class_type=Distro)
+    #    self.assertEqual(len(distro_list), 1)
+    #    self.assertTrue(distro_list[0].name == "OpenSolaris_X86.iso")
+
+    def test_mp_engine_validate(self):
+        '''
+            test_mp_engine_validate - use validate_from_docinfo=None to validate if DTD specified
+        '''
+        my_args = [common.MANIFEST_DC_DTD_PATH]
+        my_kwargs = {}
+        my_kwargs["validate_from_docinfo"] = None
+        my_kwargs["load_defaults"] = False
+
+        self.engine.register_checkpoint("manifest_parser",
+            "solaris_install/manifest/parser",
+            "ManifestParser",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_SUCCESS,
+            "ManifestParser checkpoint failed")
+
+
+    def test_mp_engine_doesnt_validate(self):
+        '''
+            test_mp_engine_doesnt_validate - execution fails if validation requested and fails
+        '''
+        my_args = [common.MANIFEST_INVALID]
+        my_kwargs = {}
+        my_kwargs["validate_from_docinfo"] = None
+        my_kwargs["load_defaults"] = False
+
+        self.engine.register_checkpoint("manifest_parser",
+            "solaris_install/manifest/parser",
+            "ManifestParser",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_FAILED,
+            "ManifestParser checkpoint should have failed")
+
+
+    def test_mp_engine_no_dtd_ref(self):
+        '''
+            test_mp_engine_no_dtd_ref - confirm error if validate_...=True and DTD not specified
+        '''
+        my_args = [common.MANIFEST_NO_DTD_REF]
+        my_kwargs = {}
+        my_kwargs["validate_from_docinfo"] = True
+
+        self.engine.register_checkpoint("manifest_parser",
+            "solaris_install/manifest/parser",
+            "ManifestParser",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_FAILED,
+            "ManifestParser checkpoint should have failed")
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/test_mp_without_engine.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,151 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestParser tests without InstallEngine'''
+
+
+import logging
+import unittest
+
+from lxml import etree
+
+import common
+from solaris_install.manifest import ManifestError
+from solaris_install.manifest.parser import ManifestParser
+from solaris_install.data_object import ParsingError
+from solaris_install.data_object.cache import DataObjectCache
+
+####################################################################
+# importing these classes causes them to be registered with the DOC
+####################################################################
+# pylint: disable-msg=W0614
+#from solaris_install.distro_const.configuration import *
+#from solaris_install.distro_const.distro_spec import *
+#from solaris_install.distro_const.execution_checkpoint import *
+from solaris_install.target.target_spec import *
+#from solaris_install.transfer.transfer_info import *
+
+
+class ManifestParserWithoutEngine(unittest.TestCase):
+    '''ManifestParser tests without InstallEngine'''
+
+    def setUp(self):
+        '''instantiate the DOC'''
+        self.doc = DataObjectCache()
+
+    def tearDown(self):
+        '''Clear the DOC'''
+        self.doc = None
+
+
+    def test_mp_no_engine_no_doc(self):
+        '''
+            test_mp_no_engine_no_doc - parse standard manifest without importing to DOC
+        '''
+        try:
+            mp_cp = ManifestParser("manifest-parser",
+                common.MANIFEST_DC,
+                validate_from_docinfo=True)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        try:
+            mp_cp.parse()
+        except ManifestError, err:
+            self.fail(str(err))
+
+
+    ### COMMENTED OUT UNTIL DC code is pushed
+    #def test_mp_no_engine_doc(self):
+    #    '''
+    #        test_mp_no_engine_doc - parse standard manifest and import to DOC
+    #    '''
+    #    try:
+    #        mp_cp = ManifestParser("manifest-parser",
+    #            common.MANIFEST_DC,
+    #            validate_from_docinfo=True)
+    #    except ManifestError, err:
+    #        self.fail(str(err))
+
+    #    try:
+    #        mp_cp.parse(doc=self.doc)
+    #    except ManifestError, err:
+    #        self.fail(str(err))
+
+    #    # Check some expected values from the cache
+    #    distro_list = self.doc.get_descendants(class_type=Distro)
+    #    self.assertTrue(len(distro_list) == 1)
+    #    self.assertTrue(distro_list[0].name == "OpenSolaris_X86.iso")
+
+
+    def test_mp_no_engine_dtd_error(self):
+        '''
+            test_mp_no_engine_dtd_error - DTDParseError raised if DTD contains invalid syntax
+        '''
+
+        try:
+            mp_cp = ManifestParser("manifest-parser",
+                common.MANIFEST_DC,
+                dtd_file=common.DTD_INVALID)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        try:
+            mp_cp.parse(doc=self.doc)
+        except ManifestError, err:
+            # Ensure exception wrapped inside ManifestError is a DTDParseError
+            self.assertTrue(isinstance(err.orig_exception, etree.DTDParseError))
+            # Check the string representation while we're at it
+            self.assertTrue(str(err).count("DTDParseError") > 0)
+        else:
+            self.fail("ManifestError should have been raised")
+
+
+    ### COMMENTED OUT UNTIL DC code is pushed
+    #def test_mp_no_engine_parse_error(self):
+    #    '''
+    #        test_mp_no_engine_parse_error - ParsingError raised when import to DOC fails
+    #    '''
+
+    #    try:
+    #        mp_cp = ManifestParser("manifest-parser",
+    #            common.MANIFEST_PARSE_ERROR,
+    #            validate_from_docinfo=False)
+    #    except ManifestError, err:
+    #        self.fail(str(err))
+
+    #    try:
+    #        mp_cp.parse(doc=self.doc)
+    #    except ManifestError, err:
+    #        # Ensure exception wrapped inside ManifestError is a ParsingError
+    #        self.assertTrue(isinstance(err.orig_exception, ParsingError))
+    #        # Check the string representation while we're at it
+    #        self.assertTrue(str(err).count("ParsingError") > 0)
+    #    else:
+    #        self.fail("ManifestError should have been raised")
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/test_mw_dry_run.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,105 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestWriter dry_run tests'''
+
+
+import logging
+import unittest
+
+import common
+from solaris_install.engine import InstallEngine
+from solaris_install.engine.test.engine_test_utils import reset_engine, \
+    get_new_engine_instance
+from solaris_install.manifest import ManifestError
+from solaris_install.manifest.writer import ManifestWriter
+
+####################################################################
+# importing these classes causes them to be registered with the DOC
+####################################################################
+# pylint: disable-msg=W0614
+#from solaris_install.distro_const.configuration import *
+#from solaris_install.distro_const.distro_spec import *
+#from solaris_install.distro_const.execution_checkpoint import *
+from solaris_install.target.target_spec import *
+#from solaris_install.transfer.transfer_info import *
+
+
+class ManifestParserWithEngine(unittest.TestCase):
+    '''ManifestWriter dry_run tests'''
+
+    def setUp(self):
+        '''instantiate the Engine so that the DOC is created'''
+        self.engine = get_new_engine_instance()
+
+    def tearDown(self):
+        '''Force all content of the DOC to be cleared.'''
+        reset_engine()
+
+
+    def test_mw_dry_run(self):
+        '''
+            test_mw_dry_run - confirm output file is changed if dry_run=True
+        '''
+        my_args = [common.MANIFEST_DC]
+        my_kwargs = {}
+        my_kwargs["validate_from_docinfo"] = True
+        my_kwargs["load_defaults"] = False
+
+        self.engine.register_checkpoint("manifest_parser",
+            "solaris_install/manifest/parser",
+            "ManifestParser",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        if status != InstallEngine.EXEC_SUCCESS:
+            self.fail("ManifestParser checkpoint failed")
+
+        # By-pass the engine for running ManifestWriter, so we
+        # can verify the object's attributes
+
+        try:
+            mw_cp = ManifestWriter("manifest-writer", common.MANIFEST_OUT_OK)
+            mw_cp.execute()
+        except ManifestError, err:
+            self.fail(str(err))
+
+        self.assertTrue(mw_cp._manifest == common.MANIFEST_OUT_OK)
+
+        # Run it again so we can be sure outfile already exists,
+        # and therefore must be changed
+        try:
+            mw_cp.execute(dry_run=True)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        self.assertTrue(mw_cp._manifest != common.MANIFEST_OUT_OK)
+
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/test_mw_invalid_params.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,62 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestWriter invalid params tests'''
+
+import logging
+import unittest
+
+import common
+from solaris_install.logger import InstallLogger
+from solaris_install.manifest import ManifestError
+from solaris_install.manifest.writer import ManifestWriter
+
+
+class InvalidParams(unittest.TestCase):
+    '''ManifestWriter invalid params tests'''
+
+    def setUp(self):
+        '''Set up logging'''
+        logging.setLoggerClass(InstallLogger)
+
+
+    def test_mw_invalid_params_xslt(self):
+        '''
+            test_mw_invalid_params_xslt - confirm error if xslt_file doesn't exist
+        '''
+        self.assertRaises(ManifestError, ManifestWriter, "manifest-writer",
+            common.MANIFEST_OUT_OK, xslt_file=common.XSLT_NON_EXISTENT)
+
+
+    def test_mw_invalid_params_dtd_file(self):
+        '''
+            test_mw_invalid_params_dtd_file - confirm error if dtd_file doesn't exist
+        '''
+        self.assertRaises(ManifestError, ManifestWriter, "manifest-writer",
+            common.MANIFEST_OUT_OK, dtd_file=common.DTD_NON_EXISTENT)
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/test_mw_with_engine.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,234 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestWriter tests using InstallEngine'''
+
+
+import logging
+import os.path
+import unittest
+
+import common
+from solaris_install.engine import InstallEngine
+from solaris_install.engine.test.engine_test_utils import reset_engine, \
+    get_new_engine_instance
+from solaris_install.manifest import ManifestError
+
+####################################################################
+# importing these classes causes them to be registered with the DOC
+####################################################################
+# pylint: disable-msg=W0614
+#from solaris_install.distro_const.configuration import *
+#from solaris_install.distro_const.distro_spec import *
+#from solaris_install.distro_const.execution_checkpoint import *
+from solaris_install.target.target_spec import *
+#from solaris_install.transfer.transfer_info import *
+
+
+class ManifestParserWithEngine(unittest.TestCase):
+    '''ManifestWriter tests using InstallEngine'''
+
+    def setUp(self):
+        '''instantiate the Engine so that the DOC is created'''
+        self.engine = get_new_engine_instance()
+
+    def tearDown(self):
+        '''Force all content of the DOC to be cleared.'''
+        reset_engine()
+
+
+    def test_mw_engine_simple(self):
+        '''
+            test_mw_engine_simple - read in and then write out a standard manifest
+        '''
+        my_args = [common.MANIFEST_DC]
+        my_kwargs = {}
+        my_kwargs["validate_from_docinfo"] = True
+        my_kwargs["load_defaults"] = False
+
+        self.engine.register_checkpoint("manifest_parser",
+            "solaris_install/manifest/parser",
+            "ManifestParser",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_SUCCESS,
+            "ManifestParser checkpoint failed")
+
+        my_args = [common.MANIFEST_OUT_OK]
+        my_kwargs = {}
+        my_kwargs["xslt_file"] = common.XSLT_DOC_TO_DC
+        my_kwargs["validate_from_docinfo"] = False
+        my_kwargs["dtd_file"] = None
+
+        self.engine.register_checkpoint("manifest_writer",
+            "solaris_install/manifest/writer",
+            "ManifestWriter",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_SUCCESS,
+            "ManifestWriter checkpoint failed")
+
+        # Confirm the output manifest looks as expected
+        self.assertTrue(common.file_line_matches(common.MANIFEST_OUT_OK,
+            1, '<dc>') == True)
+        self.assertTrue(common.file_line_matches(common.MANIFEST_OUT_OK,
+            -1, '</dc>') == True)
+
+
+    def test_mw_engine_invalid_file(self):
+        '''
+            test_mw_engine_invalid_file - try to write to file in non-existent dir
+        '''
+
+        # Repeat of test_mw_with_engine_simple, but change output file
+        # and ensure test fails
+        my_args = [common.MANIFEST_DC]
+        my_kwargs = {}
+        my_kwargs["validate_from_docinfo"] = True
+        my_kwargs["load_defaults"] = False
+
+        self.engine.register_checkpoint("manifest_parser",
+            "solaris_install/manifest/parser",
+            "ManifestParser",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_SUCCESS,
+            "ManifestParser checkpoint failed")
+
+        my_args = [common.MANIFEST_OUT_NON_EXISTENT]
+        my_kwargs = {}
+        my_kwargs["xslt_file"] = common.XSLT_DOC_TO_DC
+        my_kwargs["validate_from_docinfo"] = False
+        my_kwargs["dtd_file"] = None
+
+        self.engine.register_checkpoint("manifest_writer",
+            "solaris_install/manifest/writer",
+            "ManifestWriter",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_FAILED,
+            "ManifestWriter checkpoint should have failed")
+
+        # Confirm the output manifest wasn't created
+        self.assertTrue(os.path.exists(common.MANIFEST_OUT_NON_EXISTENT)
+            == False)
+
+
+    ### COMMENTED OUT UNTIL DC code is pushed
+    #def test_mw_engine_nonexistent_xslt(self):
+    #    '''
+    #        test_mw_engine_nonexistent_xslt - try to transform using non-existing XSLT file
+    #    '''
+
+    #    # Repeat of test_mw_with_engine_simple, but change xslt file
+    #    # and ensure test fails
+    #    my_args = [common.MANIFEST_DC]
+    #    my_kwargs = {}
+    #    my_kwargs["validate_from_docinfo"] = True
+    #    my_kwargs["load_defaults"] = False
+
+    #    self.engine.register_checkpoint("manifest_parser",
+    #        "solaris_install/manifest/parser",
+    #        "ManifestParser",
+    #        args=my_args,
+    #        kwargs=my_kwargs)
+
+    #    status = self.engine.execute_checkpoints()[0]
+
+    #    self.assertEqual(status, InstallEngine.EXEC_SUCCESS,
+    #        "ManifestParser checkpoint failed")
+
+    #    my_args = [common.MANIFEST_OUT_OK]
+    #    my_kwargs = {}
+    #    my_kwargs["xslt_file"] = common.XSLT_NON_EXISTENT
+    #    my_kwargs["validate_from_docinfo"] = False
+    #    my_kwargs["dtd_file"] = None
+
+    #    self.engine.register_checkpoint("manifest_writer",
+    #        "solaris_install/manifest/writer",
+    #        "ManifestWriter",
+    #        args=my_args,
+    #        kwargs=my_kwargs)
+
+    #    try:
+    #        status = self.engine.execute_checkpoints()[0]
+    #    except ManifestError:
+    #        pass
+    #    else:
+    #        self.fail("ManifestWriter __init__() should have failed")
+
+
+    def test_mw_engine_invalid_xslt(self):
+        '''
+            test_mw_engine_invalid_xslt - use XSLT file with invalid syntax
+        '''
+        my_args = [common.MANIFEST_DC]
+        my_kwargs = {}
+        my_kwargs["validate_from_docinfo"] = True
+        my_kwargs["load_defaults"] = False
+
+        self.engine.register_checkpoint("manifest_parser",
+            "solaris_install/manifest/parser",
+            "ManifestParser",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_SUCCESS,
+            "ManifestParser checkpoint failed")
+
+        my_args = [common.MANIFEST_OUT_OK]
+        my_kwargs = {}
+        my_kwargs["xslt_file"] = common.XSLT_INVALID
+        my_kwargs["validate_from_docinfo"] = False
+        my_kwargs["dtd_file"] = None
+
+        self.engine.register_checkpoint("manifest_writer",
+            "solaris_install/manifest/writer",
+            "ManifestWriter",
+            args=my_args,
+            kwargs=my_kwargs)
+
+        status = self.engine.execute_checkpoints()[0]
+
+        self.assertEqual(status, InstallEngine.EXEC_FAILED,
+            "ManifestWriter checkpoint should have failed")
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/test/test_mw_without_engine.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,132 @@
+#
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestWriter tests without InstallEngine'''
+
+import logging
+import unittest
+
+import common
+from solaris_install.data_object.cache import DataObjectCache
+from solaris_install.manifest import ManifestError
+from solaris_install.manifest.parser import ManifestParser
+from solaris_install.manifest.writer import ManifestWriter
+
+####################################################################
+# importing these classes causes them to be registered with the DOC
+####################################################################
+# pylint: disable-msg=W0614
+#from solaris_install.distro_const.configuration import *
+#from solaris_install.distro_const.distro_spec import *
+#from solaris_install.distro_const.execution_checkpoint import *
+from solaris_install.target.target_spec import *
+#from solaris_install.transfer.transfer_info import *
+
+
+class ManifestParserWithoutEngine(unittest.TestCase):
+    '''ManifestWriter tests without InstallEngine'''
+
+    def setUp(self):
+        '''instantiate the Engine so that the DOC is created'''
+        self.doc = DataObjectCache()
+
+    def tearDown(self):
+        '''Force all content of the DOC to be cleared.'''
+        self.doc = None
+
+
+    def test_mw_no_engine_empty_doc(self):
+        '''
+            test_mw_no_engine_empty_doc - write manifest from empty DOC
+        '''
+
+        try:
+            manifest_writer = ManifestWriter("manifest-writer",
+                common.MANIFEST_OUT_OK)
+            manifest_writer.write(self.doc)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        # Confirm the output manifest looks as expected
+        self.assertTrue(common.file_line_matches(common.MANIFEST_OUT_OK,
+            0, '<root/>') == True)
+
+
+    ### COMMENTED OUT UNTIL DC code is pushed
+    #def test_mw_no_engine_dc_simple(self):
+    #    '''
+    #        test_mw_no_engine_dc_simple - read in and write out a standard manifest
+    #    '''
+
+    #    try:
+    #        manifest_parser = ManifestParser("manifest-parser",
+    #            common.MANIFEST_DC,
+    #            validate_from_docinfo=True)
+    #        manifest_parser.parse(doc=self.doc)
+    #        manifest_writer = ManifestWriter("manifest-writer",
+    #            common.MANIFEST_OUT_OK)
+    #        manifest_writer.write(self.doc)
+    #    except ManifestError, err:
+    #        self.fail(str(err))
+
+    #    # Confirm the output manifest looks as expected
+    #    self.assertTrue(common.file_line_matches(common.MANIFEST_OUT_OK,
+    #        0, '<root>') == True)
+    #    self.assertTrue(common.file_line_matches(common.MANIFEST_OUT_OK,
+    #        -1, '</root>') == True)
+
+
+    def test_mw_no_engine_with_xslt(self):
+        '''
+            test_mw_no_engine_with_xslt - read a manifest, then transform it and write it out
+        '''
+
+        try:
+            manifest_parser = ManifestParser("manifest-parser",
+                common.MANIFEST_DC,
+                validate_from_docinfo=True)
+            manifest_parser.parse(doc=self.doc)
+
+            # We cannot test validate_from_docinfo=True on the output
+            # until the to_xml() methods of the relevant classes in
+            # other modules return XML that conforms to the schema
+
+            manifest_writer = ManifestWriter("manifest-writer",
+                common.MANIFEST_OUT_OK,
+                xslt_file=common.XSLT_DOC_TO_DC,
+                validate_from_docinfo=False)
+            manifest_writer.write(self.doc)
+        except ManifestError, err:
+            self.fail(str(err))
+
+        # Confirm the output manifest looks as expected
+        self.assertTrue(common.file_line_matches(common.MANIFEST_OUT_OK,
+            1, '<dc>') == True)
+        self.assertTrue(common.file_line_matches(common.MANIFEST_OUT_OK,
+            -1, '</dc>') == True)
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/writer/Makefile	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,63 @@
+#
+##
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+
+PYMODS		= writer.py
+
+PYCMODS		= $(PYMODS:%.py=%.pyc)
+
+ROOTPYMODS=	$(PYMODS:%=$(ROOTPYTHONVENDORSOLINSTALLMANIFEST)/%)
+
+ROOTPYCMODS=	$(PYCMODS:%=$(ROOTPYTHONVENDORSOLINSTALLMANIFEST)/%)
+
+
+CLOBBERFILES	= $(PYCMODS) 
+CLEANFILES	= $(CLOBBERFILES)
+
+include ../../Makefile.lib
+
+static:
+
+dynamic:
+
+python:
+	$(PYTHON) -m compileall -l $(@D)
+
+all:		python
+
+install_h:
+
+install:	all .WAIT \
+		$(ROOTPYTHONVENDOR) \
+		$(ROOTPYTHONVENDORSOLINSTALL) \
+		$(ROOTPYTHONVENDORSOLINSTALLMANIFEST) \
+		$(ROOTPYMODS) $(ROOTPYCMODS)
+
+lint:		lint_SRCS
+
+FRC:
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_manifest/writer/writer.py	Fri Oct 29 13:35:45 2010 +0000
@@ -0,0 +1,311 @@
+#
+##
+# 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) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''ManifestWriter Checkpoint'''
+
+import logging
+from lxml import etree
+import os
+import tempfile
+
+from solaris_install.engine import InstallEngine
+from solaris_install.engine.checkpoint import AbstractCheckpoint
+from solaris_install.manifest import ManifestError, validate_manifest
+
+
+class ManifestWriter(AbstractCheckpoint):
+    '''
+        ManifestWriter - export, transform and validate an XML manifest.
+
+
+        Summary:
+        This class implements the AbstractCheckpoint abstract base class
+        which allows it to be executed from within the InstallEngine.
+        It also provides an additional API that allow it to be run
+        outside of the InstallEngine context.
+
+
+        Initializer method:
+        The parameters of the initializer method give the location
+        of the XML manifest to be created and define what transformation
+        and validation will be performed.
+
+
+        execute() and parse() methods:
+        When running in an InstallEngine context the execute() method
+        performs the main tasks of exporting and, optionally, transforming
+        and validating the manifest.
+
+        When run outside the InstallEngine context, the write() method
+        performs these same functions.
+
+        The main difference between execute() and write() is from where
+        the DataObjectCache (DOC) reference is obtained.  execute()
+        assumes an InstallEngine singleton exists and gets the DOC
+        reference from it.  With write(), a reference to an existing DOC
+        instance must be passed in.
+
+        If an error occurs during the execute() or write() methods,
+        including XML syntax errors or failure to validate the
+        manifest, a ManifestError exception is raised.
+
+        If no errors occur, the methods simply return, with no return
+        value.  The output manifest file will be created as requested.
+
+
+        Other public methods:
+        The get_progress_estimate() method is required by the parent
+        AbstractCheckpoint class.
+
+        The cancel() method defined in AbstractCheckpoint is not
+        overridden in ManifestParser.
+    '''
+
+    def __init__(self, name, manifest, xslt_file=None,
+        validate_from_docinfo=False, dtd_file=None):
+        '''
+            Class initializer method.
+
+            Parameters:
+            - name arg is required by AbstractCheckpoint. Not used.
+            - manifest must be the path to a writable XML file
+            - xslt_file defaults to None.  Otherwise, it must be the
+              path to a readable XSLT file which will be applied to
+              the manifest XML data to transform it into the required
+              format.  Typically, this is used to structure the data
+              according to the AI or DC schema.
+            - validate_from_docinfo defaults to False.  It is only
+              relevant if, following XSL Transformation, the XML document
+              contains a reference to a DTD.  This will typically have
+              been added by the XSLT file.  If validate_from_docinfo is
+              True, the XML document will be validated against its
+              referenced DTD, if present.  If validation fails, an
+              error is raised.  If validate_from_docinfo is False or
+              no DTD is referenced, this validation is skipped.
+            - dtd_file defaults to None.  Otherwise, it must be the
+              path to a DTD file against which the manifest XML will be
+              validated before it is written out.  If validation is
+              attempted and fails, an error is raised.  This validation
+              is separate from that controlled by validate_from_docinfo.
+
+            Returns:
+            - Nothing
+
+            Raises:
+            - ManifestError is raised if invalid values are specified
+              for any paramaters or if xslt_file or dtd_file are specified
+              but do not exist.
+        '''
+
+        super(ManifestWriter, self).__init__(name)
+
+        self.logger.debug("Initializing ManifestWriter " \
+            "(manifest=%s, xslt_file=%s, " \
+            "validate_from_docinfo=%s, dtd_file=%s)",
+            manifest, xslt_file,
+            validate_from_docinfo, dtd_file)
+
+        # Check and store params
+
+        self._manifest = manifest
+
+        if ((xslt_file is not None) and
+            (not os.path.isfile(xslt_file))):
+            raise ManifestError("XSLT file [%s] is not a file" % xslt_file)
+        self._xslt_file = xslt_file
+
+        self._validate_from_docinfo = validate_from_docinfo
+
+        if ((dtd_file is not None) and
+            (not os.path.isfile(dtd_file))):
+            raise ManifestError("DTD [%s] is not a file" % dtd_file)
+        self._dtd_file = dtd_file
+
+
+    def get_progress_estimate(self):
+        '''
+            The parent class requires that this method be implemented
+            in sub-classes.
+
+            This returns an estimate of how long the execute() method
+            will take to run.
+        '''
+
+        return 1
+
+
+    def write(self, doc):
+        '''
+            This API method is not part of the AbstractCheckpoint spec.
+            It can be used to access the ManifestParser functionality outside
+            the InstallEngine context.
+
+            This method is also used as a convenience function within this
+            class to do most of the work of the execute() method.
+
+            Parameters:
+            - doc, a reference to the DataObjectCache instance from which to
+              export the manifest data.
+
+            Returns:
+            - Nothing
+              On success, this method returns; on error it raises an exception.
+
+            Raises:
+            ManifestError is raised if:
+            - xslt_file cannot be read or is not a valid XSLT file
+            - output file cannot be created or written to
+            or if validate_manifest raises an error.
+        '''
+
+        self.logger.debug("ManifestWriter.write(doc=%s) called", doc)
+
+        if self._cancel_requested.is_set():
+            self.logger.debug("Cancel requested, returning.")
+            return
+
+        # Get XML data from DOC.  This always returns something
+        xml = doc.generate_xml_manifest()
+
+        tree = etree.ElementTree(xml)
+        if self.logger.isEnabledFor(logging.DEBUG):
+            self.logger.debug("XML returned from DOC:\n%s\n",
+                etree.tostring(tree, pretty_print=True))
+
+        if self._xslt_file is not None:
+            # Perform the requested XSL Transform on the XML data
+            try:
+                xslt_doc = etree.parse(self._xslt_file)
+            except IOError, error:
+                msg = "Cannot access XSLT file [%s]" % (self._xslt_file)
+                self.logger.exception(msg)
+                self.logger.exception(error)
+                raise ManifestError(msg, orig_exception=error)
+            except etree.XMLSyntaxError, error:
+                msg = "XML syntax error in XSLT file [%s]" % \
+                    (self._xslt_file)
+                self.logger.exception(msg)
+                self.logger.exception(error)
+                raise ManifestError(msg, orig_exception=error)
+
+            transform = etree.XSLT(xslt_doc)
+
+            tree = transform(tree)
+
+        if self._cancel_requested.is_set():
+            self.logger.debug("Cancel requested, returning.")
+            return
+
+        if self._validate_from_docinfo:
+            # Validate against the DTD referenced in the headers, if any
+            if ((tree.docinfo is not None) and
+                (tree.docinfo.system_url is not None)):
+                validate_manifest(tree, tree.docinfo.system_url, self.logger)
+
+        if self._dtd_file is not None:
+            # Validate against the DTD file passed into the constructor
+            validate_manifest(tree, self._dtd_file, self.logger)
+
+        text = etree.tostring(tree, pretty_print=True)
+
+        if self._cancel_requested.is_set():
+            self.logger.debug("Cancel requested, returning.")
+            return
+
+        self.logger.debug("About to write out:\n%s\n", text)
+
+        # Write to output file
+        manifest_file = None
+        try:
+            manifest_file = open(self._manifest, mode='w')
+            manifest_file.write(text)
+        except IOError, error:
+            msg = "Cannot write to output Manifest [%s]" % (self._manifest)
+            self.logger.exception(msg)
+            self.logger.exception(error)
+            raise ManifestError(msg, orig_exception=error)
+        finally:
+            if manifest_file is not None:
+                manifest_file.close()
+
+
+    def execute(self, dry_run=False):
+        '''
+            Abstract method defined in AbstractCheckpoint class.
+
+            Exports data from InstallEngine's DataObjectCache to the
+            file named in self._manifest.
+
+            Parameters:
+            - dry_run is used to control what actions are taken if
+              self._manifest already exists.  If dry_run is False, the
+              file will be overwritten if it exists.  if dry_run is
+              True, the output will be written to a similarly-named,
+              but non-existing file.
+
+            Returns:
+            - Nothing
+              On success, this method returns; on error it raises an exception.
+
+            Raises:
+            - ManifestError is raised if unable to fetch DOC reference or
+              if an error occurs in write().
+        '''
+
+        self.logger.debug("ManifestWriter.execute(dry_run=%s) called", dry_run)
+
+        engine = InstallEngine.get_instance()
+
+        doc = engine.data_object_cache
+        if doc is None:
+            raise ManifestError("Cannot get DOC reference from InstallEngine")
+
+        if dry_run and os.path.exists(self._manifest):
+            self._manifest = _create_unique_variant(self._manifest)
+
+        self.write(doc)
+
+
+def _create_unique_variant(orig_filename):
+    '''
+        Create a variant of the passed-in filename, which does not already
+        exist.  This uses the tempfile module to create a file whose name
+        is based on orig_filename but with some random letters and numbers
+        inserted.
+    '''
+
+    dirname, filename = os.path.split(orig_filename)
+    prefix, suffix = os.path.splitext(filename)
+
+    file_desc, new_filename = \
+        tempfile.mkstemp(suffix=suffix, prefix=prefix+"_", dir=dirname)
+
+    try:
+        os.close(file_desc)
+    except IOError:
+        raise ManifestError("Could not close temp file [%s]" % new_filename)
+
+    return new_filename
--- a/usr/src/pkg/manifests/system-library-install.mf	Thu Oct 28 15:10:27 2010 -0700
+++ b/usr/src/pkg/manifests/system-library-install.mf	Fri Oct 29 13:35:45 2010 +0000
@@ -37,6 +37,7 @@
 dir path=usr/lib/python2.6/vendor-packages/solaris_install/data_object
 dir path=usr/lib/python2.6/vendor-packages/solaris_install/engine
 dir path=usr/lib/python2.6/vendor-packages/solaris_install/target
+dir path=usr/lib/python2.6/vendor-packages/solaris_install/manifest
 dir path=usr/snadm
 dir path=usr/snadm/lib
 file path=usr/lib/libaiscf.so.1
@@ -76,6 +77,12 @@
 file path=usr/lib/python2.6/vendor-packages/solaris_install/target/zfs.pyc
 file path=usr/lib/python2.6/vendor-packages/solaris_install/target/zpool.py
 file path=usr/lib/python2.6/vendor-packages/solaris_install/target/zpool.pyc
+file path=usr/lib/python2.6/vendor-packages/solaris_install/manifest/__init__.py
+file path=usr/lib/python2.6/vendor-packages/solaris_install/manifest/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/solaris_install/manifest/parser.py
+file path=usr/lib/python2.6/vendor-packages/solaris_install/manifest/parser.pyc
+file path=usr/lib/python2.6/vendor-packages/solaris_install/manifest/writer.py
+file path=usr/lib/python2.6/vendor-packages/solaris_install/manifest/writer.pyc
 file path=usr/snadm/lib/libspmicommon.so.1
 license cr_Sun license=cr_Sun
 link path=usr/lib/libaiscf.so target=libaiscf.so.1
--- a/usr/src/tools/tests/tests.nose	Thu Oct 28 15:10:27 2010 -0700
+++ b/usr/src/tools/tests/tests.nose	Fri Oct 29 13:35:45 2010 +0000
@@ -30,4 +30,4 @@
 # the files in that directory should begine with "test_". Files
 # containing in-line doc-tests should be added explicitly.
 
-tests=lib/liberrsvc_pymod/test/,cmd/ai-webserver/test/,cmd/text-install/osol_install/text_install/test/,cmd/installadm/test/,cmd/installadm/installadm_common.py,lib/install_utils/test/,lib/libict_pymod/test/,lib/install_logging_pymod/test,lib/install_doc/test,lib/install_engine/test
+tests=lib/liberrsvc_pymod/test/,cmd/ai-webserver/test/,cmd/text-install/osol_install/text_install/test/,cmd/installadm/test/,cmd/installadm/installadm_common.py,lib/install_utils/test/,lib/libict_pymod/test/,lib/install_logging_pymod/test,lib/install_doc/test,lib/install_engine/test,lib/install_manifest/test/