16423 Updates to AI schema should be made
authorSue Sohn <Susan.Sohn@Oracle.COM>
Fri, 20 Aug 2010 11:31:18 -0600
changeset 862 e9f31f2f2f2d
parent 861 ccd399d2c6f7
child 863 5a915c215754
16423 Updates to AI schema should be made 15449 installadm add validates combined manifest against image-specific schema as well as schema in /usr/share/auto_install/ 6975043 separate criteria and ai manifest 6975686 installadm list shows value rather than range if lower bound is 0
usr/src/cmd/ai-webserver/AI_database.py
usr/src/cmd/ai-webserver/Makefile
usr/src/cmd/ai-webserver/criteria_schema.rng
usr/src/cmd/ai-webserver/publish-manifest.py
usr/src/cmd/ai-webserver/publish_manifest.py
usr/src/cmd/ai-webserver/set_criteria.py
usr/src/cmd/ai-webserver/test/test_ai_database.py
usr/src/cmd/ai-webserver/test/test_publish_manifest.py
usr/src/cmd/ai-webserver/test/test_set_criteria.py
usr/src/cmd/ai-webserver/verifyXML.py
usr/src/cmd/auto-install/Makefile
usr/src/cmd/auto-install/ai.dtd
usr/src/cmd/auto-install/ai_manifest.defval.xml
usr/src/cmd/auto-install/ai_manifest.rng
usr/src/cmd/auto-install/ai_manifest.xml
usr/src/cmd/auto-install/ai_parse_manifest.py
usr/src/cmd/auto-install/auto_ddu_lib.c
usr/src/cmd/auto-install/auto_install.c
usr/src/cmd/auto-install/auto_install.h
usr/src/cmd/auto-install/auto_parse.c
usr/src/cmd/auto-install/auto_td.c
usr/src/cmd/auto-install/configuration.dtd
usr/src/cmd/auto-install/default.xml
usr/src/cmd/auto-install/software.dtd
usr/src/cmd/auto-install/svc/auto-installer
usr/src/cmd/auto-install/target.dtd
usr/src/cmd/auto-install/xslt/README
usr/src/cmd/auto-install/xslt/old-to-new.py
usr/src/cmd/auto-install/xslt/old-to-new.xslt
usr/src/cmd/distro_const/text_install/text_live.xml
usr/src/cmd/gui-install/src/orchestrator-wrappers.c
usr/src/cmd/gui-install/src/orchestrator-wrappers.h
usr/src/cmd/install-tools/ManifestServ.py
usr/src/cmd/installadm/installadm.c
usr/src/cmd/installadm/installadm.h
usr/src/cmd/installadm/list.py
usr/src/lib/install_utils/DefValProc.py
usr/src/lib/install_utils/ManifestServ.py
usr/src/lib/install_utils/TreeAcc.py
usr/src/lib/install_utils/test/test_manifest_serv.py
usr/src/lib/libict/ict.c
usr/src/man/installadm.1m.txt
usr/src/pkg/manifests/install-installadm.mf
usr/src/pkg/manifests/system-install-auto-install-auto-install-common.mf
usr/src/pkg/manifests/system-install-auto-install.mf
usr/src/tools/tests/README
usr/src/tools/tests/config.nose
usr/src/tools/tests/slim_test
usr/src/tools/tests/tests.nose
--- a/usr/src/cmd/ai-webserver/AI_database.py	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/ai-webserver/AI_database.py	Fri Aug 20 11:31:18 2010 -0600
@@ -18,8 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 """
 
 A/I Database Routines
@@ -266,8 +265,8 @@
     Use to create a generator which provides the names of manifests
     in the DB
     """
-    # Whether the DBrequest should be in or out of the for loop depends on if doing
-    # one large SQL query and iterating the responses (but holding the
+    # Whether the DBrequest should be in or out of the for loop depends on if
+    # doing one large SQL query and iterating the responses (but holding the
     # response in memory) is better than requesting row by row, and
     # doing more DB transactions in less memory
     for i in range(numManifests(queue)):
@@ -297,10 +296,12 @@
     return(query.respnse())
 
 def getSpecificCriteria(queue, criteria, criteria2=None,
-                        provideManNameAndInstance=False):
+                        provideManNameAndInstance=False,
+			excludeManifests=None):
     """
     Returns the criteria specified as an iterable list (can provide a second
-    criteria to return a range (i.e. MIN and MAX memory)
+    criteria to return a range (i.e. MIN and MAX memory).  The excludeManifests
+    argument filters out the rows with the manifest names given in that list.
     """
     if provideManNameAndInstance:
         queryStr = "SELECT name, instance, "
@@ -311,13 +312,13 @@
             # for hexadecimal values we need to use the SQL HEX() function to
             # properly return a hex value
             queryStr += ("HEX(" + criteria + "), HEX(" + criteria2 +
-                         ") FROM manifests WHERE " + criteria +
-                         " IS NOT NULL OR " + criteria2 + " IS NOT NULL")
+                         ") FROM manifests WHERE (" + criteria +
+                         " IS NOT NULL OR " + criteria2 + " IS NOT NULL)")
         else:
             # this is a decimal range value
             queryStr += (criteria + ", " + criteria2 +
-                         " FROM manifests WHERE " + criteria +
-                         " IS NOT NULL OR " + criteria2 + " IS NOT NULL")
+                         " FROM manifests WHERE (" + criteria +
+                         " IS NOT NULL OR " + criteria2 + " IS NOT NULL)")
     else:
         if criteria.endswith('mac'):
             # for hexadecimal values we need to use the SQL HEX() function to
@@ -328,6 +329,11 @@
             # this is a decimal range value or string
             queryStr += (criteria + " FROM manifests WHERE " + criteria +
                          " IS NOT NULL")
+
+    if excludeManifests is not None:
+        for manifest in excludeManifests:
+            queryStr += " AND name IS NOT '" + manifest + "'"
+
     query = DBrequest(queryStr)
     queue.put(query)
     query.waitAns()
@@ -405,6 +411,19 @@
                 yield str(colName)
         return
 
+def isRangeCriteria(queue, name):
+    """
+    Returns True if the criteria 'name' is a range criteria in the DB.
+    Returns False otherwise.
+    """
+    criteria = getCriteria(queue, onlyUsed=False, strip=False)
+    for crit in criteria:
+	if crit.startswith('MIN'):
+	    if name == crit.replace('MIN', ''):
+		return True
+
+    return False
+
 def getManifestCriteria(name, instance, queue, humanOutput=False,
                         onlyUsed=True):
     """
--- a/usr/src/cmd/ai-webserver/Makefile	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/ai-webserver/Makefile	Fri Aug 20 11:31:18 2010 -0600
@@ -20,8 +20,7 @@
 #
 
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.cmd
@@ -41,9 +40,11 @@
 
 SCHEMAS=	criteria_schema.rng
 
-PYTHON_EXECS= delete-manifest \
-			  publish-manifest \
-			  webserver
+PYTHON_EXECS=	delete-manifest \
+		webserver
+
+
+PYTHON_MODULES=	publish_manifest.py set_criteria.py
 
 ROOTPYMODULES= $(PYMODULES:%=$(ROOTPYTHONVENDORINSTALLAI)/%)
 
@@ -53,6 +54,8 @@
 
 ROOTPROGS= $(PYTHON_EXECS:%=$(ROOTUSRLIBINSTALLADM)/%)
 
+ROOTPYSCRIPTS=	$(PYTHON_MODULES:%=$(ROOTUSRLIBINSTALLADM)/%)
+
 ROOTSCHEMAS= $(SCHEMAS:%=$(ROOTAUTOINST)/%)
 
 all: python $(PYTHON_EXECS)
@@ -67,6 +70,7 @@
 			$(ROOTPYTHONVENDORINSTALLAI) \
 			$(ROOTPYMODULES) \
 			$(ROOTPYCMODULES) \
+			$(ROOTPYSCRIPTS) \
 			$(ROOTVARINSTADM) \
 			$(ROOTWEBSERVER_FILES) \
 			$(ROOTSCHEMAS)
--- a/usr/src/cmd/ai-webserver/criteria_schema.rng	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/ai-webserver/criteria_schema.rng	Fri Aug 20 11:31:18 2010 -0600
@@ -18,8 +18,7 @@
 
 CDDL HEADER END
 
-Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-Use is subject to license terms.
+Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 -->
 
 <!--
@@ -37,70 +36,16 @@
     datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
 	<start>
 		<element name="ai_criteria_manifest">
-			<!-- Allow the following in any order -->
-			<interleave>
-				<!--
-				    The manifest may or may not
-				    have criteria in it
-				-->
-				<zeroOrMore>
-					<ref name="nm_auto_install_criteria"/>
-				</zeroOrMore>
-				<!--
-				    One need not specify an A/I manifest in all
-				    applications, an SC manifest is required
-				    when an A/I manifest is supplied.
-				-->
-				<optional>
-					<ref name="nm_ai_manifest_src"/>
-					<ref name="nm_sc_manifest_src"/>
-				</optional>
-			</interleave>
+			<!--
+			    The manifest may or may not
+			    have criteria in it.
+			-->
+			<zeroOrMore>
+				<ref name="nm_auto_install_criteria"/>
+			</zeroOrMore>
 		</element>
 	</start>
 
-	<define name="nm_ai_manifest_src">
-		<choice>
-			<!--
-			    Either embed or point to an A/I manifest
-			-->
-			<element name="ai_manifest_file">
-				<attribute name="URI">
-					<data type="anyURI"/>
-				</attribute>
-			</element>
-			<element name="ai_embedded_manifest">
-				<externalRef href="ai_manifest.rng"/>
-			</element>
-		</choice>
-	</define>
-
-	<define name="nm_sc_manifest_src">
-		<choice>
-			<!--
-			    Either embed or point to an SC manifest
-			-->
-			<element name="sc_manifest_file">
-				<attribute name="name">
-					<text/>
-				</attribute>
-				<attribute name="URI">
-					<data type="anyURI"/>
-				</attribute>
-			</element>
-			<element name="sc_embedded_manifest">
-				<attribute name="name">
-					<text/>
-				</attribute>
-				<!--
-				    Note: the embedded manifest being DTD
-				    based can not be verified and as such
-				    should be included inside comment tags
-				-->
-			</element>
-		</choice>
-	</define>
-
 	<!--
 	=======================================================================
 	Parameters used for Automatic Installation criteria
--- a/usr/src/cmd/ai-webserver/publish-manifest.py	Tue Aug 17 18:22:44 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1084 +0,0 @@
-#!/usr/bin/python2.6
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-"""
-
-A/I Publish_Manifest
-
-"""
-
-import os.path
-import sys
-import StringIO
-import gettext
-import lxml.etree
-import hashlib
-from optparse import OptionParser
-
-import osol_install.auto_install.AI_database as AIdb
-import osol_install.auto_install.verifyXML as verifyXML
-import osol_install.libaiscf as smf
-
-INFINITY = str(0xFFFFFFFFFFFFFFFF)
-IMG_AI_MANIFEST_SCHEMA = "auto_install/ai_manifest.rng"
-SYS_AI_MANIFEST_SCHEMA = "/usr/share/auto_install/ai_manifest.rng"
-
-def parse_options():
-    """
-    Parse and validate options
-    Args: None
-    Returns: the DataFiles object populated and initialized
-    Raises: The DataFiles initialization of manifest(s) A/I, SC, SMF looks for
-            many error conditions and, when caught, are flagged to the user
-            via raising SystemExit exceptions.
-    """
-
-    usage = _("usage: %prog service_name criteria_manifest")
-    parser = OptionParser(usage=usage)
-    # since no options are specified simply retrieve the args list
-    # args should be a list with indices:
-    # 0 - service name
-    # 1 - manifest path to work with
-    args = parser.parse_args()[1]
-
-    # check that we got the install service's name and
-    # a criteria manifest
-    if len(args) != 2:
-        parser.print_help()
-        sys.exit(1)
-
-    # get an AIservice object for requested service
-    try:
-        svc = smf.AIservice(smf.AISCF(FMRI="system/install/server"), args[0])
-    except KeyError:
-        raise SystemExit(_("Error:\tFailed to find service %s") % args[0])
-
-    # argument two is the criteria manifest
-    crit_manifest = args[1]
-
-    # get the service's data directory path and imagepath
-    try:
-        image_path = svc['image_path']
-        # txt_record is of the form "aiwebserver=jumprope:46503" so split on ":"
-        # and take the trailing portion for the port number
-        port = svc['txt_record'].rsplit(':')[-1]
-    except KeyError:
-        raise SystemExit(_("SMF data for service %s is corrupt.\n") %
-                         args[0])
-    service_dir = os.path.abspath("/var/ai/" + port)
-
-    # check that the service and imagepath directories exist,
-    # and the AI.db, criteria_schema.rng and ai_manifest.rng files
-    # are present otherwise the service is misconfigured
-    if not (os.path.isdir(service_dir) and
-            os.path.exists(os.path.join(service_dir, "AI.db"))):
-        raise SystemExit("Error:\tNeed a valid A/I service directory")
-
-    files = DataFiles(service_dir = service_dir, image_path = image_path,
-                      database_path = os.path.join(service_dir, "AI.db"),
-                      criteria_path = crit_manifest)
-
-    return(files)
-
-def find_colliding_criteria(files):
-    """
-    Returns: A dictionary of colliding criteria with keys being manifest name
-             and instance tuples and values being the DB column names which
-             collided
-    Args: DataFiles object with a valid _criteria_root and database object
-    Raises: SystemExit if: criteria is not found in database
-                           value is not valid for type (integer and hexadecimal
-                             checks)
-                           range is improper
-    """
-    # define convenience strings:
-    class fields(object):
-        # manifest name is row index 0
-        MANNAME = 0
-        # manifest instance is row index 1
-        MANINST = 1
-        # criteria is row index 2 (when a single valued criteria)
-        CRIT = 2
-        # minimum criteria is row index 2 (when a range valued criteria)
-        MINCRIT = 2
-        # maximum criteria is row index 3 (when a range valued criteria)
-        MAXCRIT = 3
-
-    # collisions is a dictionary to hold keys of the form (manifest name,
-    # instance) which will point to a comma-separated string of colliding
-    # criteria
-    collisions = dict()
-
-    # verify each range criteria in the manifest is well formed and collect
-    # collisions with database entries
-    for crit in files.criteria:
-        # gather this criteria's values from the manifest
-        man_criterion = files.criteria[crit]
-
-        # check "value" criteria here (check the criteria exists in DB, and
-        # then find collisions)
-        if not isinstance(man_criterion, list):
-            # only check criteria in use in the DB
-            if crit not in AIdb.getCriteria(files.database.getQueue(),
-                                            onlyUsed=False, strip=False):
-                raise SystemExit(_("Error:\tCriteria %s is not a " +
-                                   "valid criteria!") % crit)
-
-            # get all values in the database for this criteria (and
-            # manifest/instance paris for each value)
-            db_criteria = AIdb.getSpecificCriteria(
-                files.database.getQueue(), crit, None,
-                provideManNameAndInstance=True)
-
-            # will iterate over a list of the form [manName, manInst, crit,
-            # None]
-            for row in db_criteria:
-                # check if the database and manifest values differ
-                if(str(row[fields.CRIT]).lower() == str(man_criterion).lower()):
-                    # record manifest name, instance and criteria name
-                    try:
-                        collisions[row[fields.MANNAME],
-                                   row[fields.MANINST]] += crit + ","
-                    except KeyError:
-                        collisions[row[fields.MANNAME],
-                                   row[fields.MANINST]] = crit + ","
-
-        # This is a range criteria.  (Check that ranges are valid, that
-        # "unbounded" gets set to 0/+inf, ensure the criteria exists
-        # in the DB, then look for collisions.)
-        else:
-            # check for a properly ordered range (with unbounded being 0 or
-            # Inf.) but ensure both are not unbounded
-            if(
-               # Check for a range of -inf to inf -- not a valid range
-               (man_criterion[0] == "unbounded" and
-                man_criterion[1] == "unbounded"
-               ) or
-               # Check min > max -- range order reversed
-               (
-                (man_criterion[0] != "unbounded" and
-                 man_criterion[1] != "unbounded"
-                ) and
-                (man_criterion[0] > man_criterion[1])
-               )
-              ):
-                raise SystemExit(_("Error:\tCriteria %s "
-                                   "is not a valid range (MIN > MAX) or "
-                                   "(MIN and MAX unbounded).") % crit)
-
-            # Clean-up NULL's and changed "unbounded"s to 0 and
-            # really large numbers in case this Python does
-            # not support IEEE754.  Note "unbounded"s are already
-            # converted to lower case during manifest processing.
-            if man_criterion[0] == "unbounded":
-                man_criterion[0] = "0"
-            if man_criterion[1] == "unbounded":
-                man_criterion[1] = INFINITY
-            if crit == "mac":
-                # convert hex mac address (w/o colons) to a number
-                try:
-                    man_criterion[0] = long(str(man_criterion[0]).upper(), 16)
-                    man_criterion[1] = long(str(man_criterion[1]).upper(), 16)
-                except ValueError:
-                    raise SystemExit(_("Error:\tCriteria %s "
-                                       "is not a valid hexadecimal value") %
-                                     crit)
-
-            else:
-                # this is a decimal value
-                try:
-                    man_criterion = [long(str(man_criterion[0]).upper()),
-                                     long(str(man_criterion[1]).upper())]
-                except ValueError:
-                    raise SystemExit(_("Error:\tCriteria %s "
-                                       "is not a valid integer value") % crit)
-
-            # check to see that this criteria exists in the database columns
-            if ('MIN' + crit not in AIdb.getCriteria(
-                files.database.getQueue(), onlyUsed=False, strip=False))\
-            and ('MAX' + crit not in AIdb.getCriteria(
-                files.database.getQueue(), onlyUsed=False,  strip=False)):
-                raise SystemExit(_("Error:\tCriteria %s is not a " +
-                                   "valid criteria!") % crit)
-            db_criteria = AIdb.getSpecificCriteria(
-                files.database.getQueue(), 'MIN' + crit, 'MAX' + crit,
-                provideManNameAndInstance=True)
-
-            # will iterate over a list of the form [manName, manInst, mincrit,
-            # maxcrit]
-            for row in db_criteria:
-                # arbitrarily large number in case this Python does
-                # not support IEEE754
-                db_criterion = ["0", INFINITY]
-
-                # now populate in valid database values (i.e. non-NULL values)
-                if row[fields.MINCRIT]:
-                    db_criterion[0] = row[fields.MINCRIT]
-                if row[fields.MAXCRIT]:
-                    db_criterion[1] = row[fields.MAXCRIT]
-                if crit == "mac":
-                    # use a hexadecimal conversion
-                    db_criterion = [long(str(db_criterion[0]), 16),
-                                    long(str(db_criterion[1]), 16)]
-                else:
-                    # these are decimal numbers
-                    db_criterion = [long(str(db_criterion[0])),
-                                    long(str(db_criterion[1]))]
-
-                # these three criteria can determine if there's a range overlap
-                if((man_criterion[1] >= db_criterion[0] and
-                   db_criterion[1] >= man_criterion[0]) or
-                   man_criterion[0] == db_criterion[1]):
-                    # range overlap so record the collision
-                    try:
-                        collisions[row[fields.MANNAME],
-                                   row[fields.MANINST]] += "MIN" + crit + ","
-                        collisions[row[fields.MANNAME],
-                                   row[fields.MANINST]] += "MAX" + crit + ","
-                    except KeyError:
-                        collisions[row[fields.MANNAME],
-                                   row[fields.MANINST]] = "MIN" + crit + ","
-                        collisions[row[fields.MANNAME],
-                                   row[fields.MANINST]] += "MAX" + crit + ","
-    return collisions
-
-def find_colliding_manifests(files, collisions):
-    """
-    For each manifest/instance pair in collisions check that the manifest
-    criteria diverge (i.e. are not exactly the same) and that the ranges do not
-    collide for ranges.
-    Raises if: a range collides, or if the manifest has the same criteria as a
-    manifest already in the database (SystemExit raised)
-    Returns: Nothing
-    Args: files - DataFiles object with vaild _criteria_root and database
-                  object
-          collisions - a dictionary with collisions, as produced by
-                       find_colliding_criteria()
-    """
-    # check every manifest in collisions to see if manifest collides (either
-    # identical criteria, or overlaping ranges)
-    for man_inst in collisions:
-        # get all criteria from this manifest/instance pair
-        db_criteria = AIdb.getManifestCriteria(man_inst[0],
-                                               man_inst[1],
-                                               files.database.getQueue(),
-                                               humanOutput=True,
-                                               onlyUsed=False)
-
-        # iterate over every criteria in the database
-        for crit in AIdb.getCriteria(files.database.getQueue(),
-                                     onlyUsed=False, strip=False):
-
-            # Get the criteria name (i.e. no MIN or MAX)
-            crit_name = crit.replace('MIN', '', 1).replace('MAX', '', 1)
-            # Set man_criterion to the key of the DB criteria or None
-            man_criterion = files.criteria[crit_name]
-            if man_criterion and crit.startswith('MIN'):
-                man_criterion = man_criterion[0]
-            elif man_criterion and crit.startswith('MAX'):
-                man_criterion = man_criterion[1]
-
-            # set the database criteria
-            if db_criteria[str(crit)] == '':
-                # replace database NULL's with a Python None
-                db_criterion = None
-            else:
-                db_criterion = db_criteria[str(crit)]
-
-            # Replace unbounded's in the criteria (i.e. 0/+inf)
-            # with a Python None.
-            if isinstance(man_criterion, basestring) and \
-               man_criterion == "unbounded":
-                man_criterion = None
-
-            # check to determine if this is a range collision by using
-            # collisions and if not are the manifests divergent
-
-            if((crit.startswith('MIN') and
-                collisions[man_inst].find(crit + ",") != -1) or
-               (crit.startswith('MAX') and
-                collisions[man_inst].find(crit + ",") != -1)
-              ):
-                if (str(db_criterion).lower() != str(man_criterion).lower()):
-                    raise SystemExit(_("Error:\tManifest has a range "
-                                       "collision with manifest:%s/%i"
-                                       "\n\tin criteria: %s!") %
-                                     (man_inst[0], man_inst[1],
-                                      crit.replace('MIN', '', 1).
-                                      replace('MAX', '', 1)))
-
-            # the range did not collide or this is a single value (if we
-            # differ we can break out knowing we diverge for this
-            # manifest/instance)
-            elif(str(db_criterion).lower() != str(man_criterion).lower()):
-                # manifests diverge (they don't collide)
-                break
-
-        # end of for loop and we never broke out (diverged)
-        else:
-            raise SystemExit(_("Error:\tManifest has same criteria as " +
-                               "manifest:%s/%i!") %
-                             (man_inst[0], man_inst[1]))
-
-def insert_SQL(files):
-    """
-    Ensures all data is properly sanitized and formatted, then inserts it into
-    the database
-    Args: None
-    Returns: None
-    """
-    query = "INSERT INTO manifests VALUES("
-
-    # add the manifest name to the query string
-    query += "'" + AIdb.sanitizeSQL(files.manifest_name) + "',"
-    # check to see if manifest name is alreay in database (affects instance
-    # number)
-    if AIdb.sanitizeSQL(files.manifest_name) in \
-        AIdb.getManNames(files.database.getQueue()):
-            # database already has this manifest name get the number of
-            # instances
-        instance = AIdb.numInstances(AIdb.sanitizeSQL(files.manifest_name),
-                                     files.database.getQueue())
-
-    # this a new manifest
-    else:
-        instance = 0
-
-    # actually add the instance to the query string
-    query += str(instance) + ","
-
-    # we need to fill in the criteria or NULLs for each criteria the database
-    # supports (so iterate over each criteria)
-    for crit in AIdb.getCriteria(files.database.getQueue(),
-                                 onlyUsed=False, strip=False):
-        # for range values trigger on the MAX criteria (skip the MIN's
-        # arbitrary as we handle rows in one pass)
-        if crit.startswith('MIN'):
-            continue
-
-        # get the values from the manifest
-        values = files.criteria[crit.replace('MAX', '', 1)]
-
-        # if the values are a list this is a range
-        if isinstance(values, list):
-            for value in values:
-                # translate "unbounded" to a database NULL
-                if value == "unbounded":
-                    query += "NULL,"
-                # we need to deal with mac addresses specially being
-                # hexadecimal
-                elif crit.endswith("mac"):
-                    # need to insert with hex operand x'<val>'
-                    # use an upper case string for hex values
-                    query += "x'" + AIdb.sanitizeSQL(str(value).upper())+"',"
-                else:
-                    query += AIdb.sanitizeSQL(str(value).upper()) + ","
-
-        # this is a single criteria (not a range)
-        elif isinstance(values, basestring):
-            # translate "unbounded" to a database NULL
-            if values == "unbounded":
-                query += "NULL,"
-            else:
-                # use lower case for text strings
-                query += "'" + AIdb.sanitizeSQL(str(values).lower()) + "',"
-
-        # the critera manifest didn't specify this criteria so fill in NULLs
-        else:
-            # use the criteria name to determine if this is a range
-            if crit.startswith('MAX'):
-                query += "NULL,NULL,"
-            # this is a single value
-            else:
-                query += "NULL,"
-
-    # strip trailing comma and close parentheses
-    query = query[:-1] + ")"
-
-    # update the database
-    query = AIdb.DBrequest(query, commit=True)
-    files.database.getQueue().put(query)
-    query.waitAns()
-    # in case there's an error call the response function (which will print the
-    # error)
-    query.getResponse()
-
-def do_default(files):
-    """
-    Removes old default.xml after ensuring proper format of new manifest
-    (does not copy new manifest over -- see place_manifest)
-    Args: None
-    Returns: None
-    Raises if: Manifest has criteria, old manifest can not be removed (exits
-               with SystemExit)
-    """
-    # check to see if any criteria is present -- if so, it can not be a default
-    # manifest (as they do not have criteria)
-    if files.criteria:
-        raise SystemExit(_("Error:\tCan not use AI criteria in a default " +
-                           "manifest"))
-    # remove old manifest
-    try:
-        os.remove(os.path.join(files.get_service(), 'AI_data', 'default.xml'))
-    except IOError, e:
-        raise SystemExit(_("Error:\tUnable to remove default.xml:\n\t%s") % e)
-
-def place_manifest(files):
-    """
-    Compares src and dst manifests to ensure they are the same; if manifest
-    does not yet exist, copies new manifest into place and sets correct
-    permissions and ownership
-    Args: None
-    Returns: None
-    Raises if: src and dst manifests differ (in MD5 sum), unable to write dst
-               manifest (raises SystemExit -- no clean up of database performed)
-    """
-    manifest_path = os.path.join(files.get_service(), "AI_data",
-                                files.manifest_name)
-
-    # if the manifest already exists see if it is different from what was
-    # passed in. If so, warn the user that we're using the existing manifest
-    if os.path.exists(manifest_path):
-        old_manifest = open(manifest_path, "r")
-        existing_MD5 = hashlib.md5("".join(old_manifest.readlines())).digest()
-        old_manifest.close()
-        current_MD5 = hashlib.md5(lxml.etree.tostring(files._AI_root,
-                                 pretty_print=True, encoding=unicode)).digest()
-        if existing_MD5 != current_MD5:
-            raise SystemExit(_("Error:\tNot copying manifest, source and " +
-                               "current versions differ -- criteria in place."))
-
-    # the manifest does not yet exist so write it out
-    else:
-        try:
-            new_man = open(manifest_path, "w")
-            new_man.writelines('<ai_criteria_manifest>\n')
-            new_man.writelines('\t<ai_embedded_manifest>\n')
-            new_man.writelines(lxml.etree.tostring(
-                                   files._AI_root, pretty_print=True,
-                                   encoding=unicode))
-            new_man.writelines('\t</ai_embedded_manifest>\n')
-            # write out each SMF SC manifest
-            for key in files._smfDict:
-                new_man.writelines('\t<sc_embedded_manifest name = "%s">\n'%
-                                       key)
-                new_man.writelines("\t\t<!-- ")
-                new_man.writelines("<?xml version='1.0'?>\n")
-                new_man.writelines(lxml.etree.tostring(files._smfDict[key],
-                                       pretty_print=True, encoding=unicode))
-                new_man.writelines('\t -->\n')
-                new_man.writelines('\t</sc_embedded_manifest>\n')
-            new_man.writelines('\t</ai_criteria_manifest>\n')
-            new_man.close()
-        except IOError, e:
-            raise SystemExit(_("Error:\tUnable to write to dest. "
-                               "manifest:\n\t%s") % e)
-
-    # change read for all and write for owner
-    os.chmod(manifest_path, 0644)
-    # change to user/group root (uid/gid 0)
-    os.chown(manifest_path, 0, 0)
-
-class DataFiles(object):
-    """
-    Class to contain and work with data files necessary for program
-    """
-    # schema for validating an AI criteria manifest
-    criteriaSchema = "/usr/share/auto_install/criteria_schema.rng"
-    # DTD for validating an SMF SC manifest
-    smfDtd = "/usr/share/lib/xml/dtd/service_bundle.dtd.1"
-
-
-    def __init__(self, service_dir = None, image_path = None,
-                 database_path = None, criteria_path = None):
-        """
-        Initialize DataFiles instance. All parameters optional, however, proper
-        setup order asurred, if all data provided upon instantiation.
-        """
-
-        #
-        # State variables
-        #################
-        #
-
-        # Variable to cache criteria class for criteria property
-        self._criteria_cache = None
-
-        #
-        # File system path variables
-        ############################
-        #
-
-        # Check AI Criteria Schema exists
-        if not os.path.exists(self.criteriaSchema):
-            raise SystemExit(_("Error:\tUnable to find criteria_schema: " +
-                               "%s") % self.criteriaSchema)
-
-        # Check SC manifest SMF DTD exists
-        if not os.path.exists(self.smfDtd):
-            raise SystemExit(_("Error:\tUnable to find SMF system " +
-                               "configuration DTD: %s") % self.smfDtd)
-
-        # A/I Manifest Schema
-        self._AIschema = None
-
-        # Holds path to service directory (i.e. /var/ai/46501)
-        self._service = None
-        if service_dir:
-            self.service = service_dir
-
-        # Holds path to AI image
-        self._imagepath = None
-        if image_path:
-            self.image_path = image_path
-            # set the AI schema once image_path is set
-            self.set_AI_schema()
-
-        # Holds database object for criteria database
-        self._db = None
-        if database_path:
-            # Set Database Path and Open SQLite3 Object
-            self.database = database_path
-            # verify the database's table/column structure (or exit if errors)
-            self.database.verifyDBStructure()
-
-        #
-        # XML DOM variables
-        ###################
-        #
-
-        #
-        # Criteria manifest setup
-        #
-
-        # Holds DOM for criteria manifest
-        self._criteria_root = None
-
-        # Holds path for criteria manifest
-        self.criteria_path = criteria_path
-        # find SC manifests from the criteria manifest and validate according
-        # to the SMF DTD (exit if errors)
-        if criteria_path:
-            # sets _criteria_root DOM
-            self.verifyCriteria()
-
-        #
-        # SC manifest setup
-        #
-
-        # Holds DOMs for SC manifests
-        self._smfDict = dict()
-
-        # if we were provided a criteria manifest, look for a SC manifests
-        # specified by the criteria manifest
-        if self._criteria_root:
-            # sets _smfDict DOMs
-            self.find_SC_from_crit_man()
-
-        #
-        # AI manifest setup
-        #
-
-        # Holds DOM for AI manifest
-        self._AI_root = None
-
-        # Holds path to AI manifest being published (may not be set if an
-        # embedded manifest)
-        self._manifest = None
-
-        # if we were provided a criteria manifest, look for an A/I manifest
-        # specified by the criteria manifest
-        if self._criteria_root:
-            # this will set _manifest to be the AI manifest path (if a file),
-            # set _AI_root to the correct location in the criteria DOM (if
-            # embedded), or exit (if unable to find an AI manifest)
-            self.find_AI_from_criteria()
-            # this will verify the _AI_root DOM and exit on error
-            self.verify_AI_manifest()
-
-    # overload the _criteria class to be a list with a special get_item to act
-    # like a dictionary
-    class _criteria(list):
-        """
-        Wrap list class to provide lookups in the criteria file when
-        requested
-        """
-        def __init__(self, criteria_root):
-            # store the criteria manifest DOM root
-            self._criteria_root = criteria_root
-            # call the _init_() for the list class with a generator provided by
-            # find_criteria() to populate this _criteria() instance.
-            super(DataFiles._criteria, self).__init__(self.find_criteria())
-
-        def __getitem__(self, key):
-            """
-            Look up a requested criteria (akin to dictionary access) but for an
-            uninitialized key will not raise an exception but return None)
-            """
-            return self.get_criterion(key)
-
-        def find_criteria(self):
-            """
-            Find criteria from the criteria manifest
-            Returns: A generator providing all criteria name attributes from
-                     <ai_criteria> tags
-            """
-            root = self._criteria_root.findall(".//ai_criteria")
-
-            # actually find criteria
-            for tag in root:
-                for child in tag.getchildren():
-                    if (child.tag == "range" or child.tag == "value") and \
-                        child.text is not None:
-                        # criteria names are lower case
-                        yield tag.attrib['name'].lower()
-                    # should not happen according to schema
-                    else:
-                        raise AssertionError(_(
-                            "Criteria contains no values"))
-
-        def get_criterion(self, criterion):
-            """
-            Return criterion out of the criteria manifest
-            Returns: A list for range criterion with a min and max entry
-                     A string for value criterion
-            """
-            source = self._criteria_root
-            for tag in source.getiterator('ai_criteria'):
-                crit = tag.get('name')
-                # compare criteria name case-insensitive
-                if crit.lower() == criterion.lower():
-                    for child in tag.getchildren():
-                        if child.tag == "range":
-                            # this is a range response (split on white space)
-                            return child.text.split()
-                        elif child.tag == "value":
-                            # this is a value response (strip white space)
-                            return child.text.strip()
-                        # should not happen according to schema
-                        elif child.text is None:
-                            raise AssertionError(_(
-                                "Criteria contains no values"))
-            return None
-
-        # disable trying to update criteria
-        __setitem__ = None
-        __delitem__ = None
-
-    @property
-    def criteria(self):
-        """
-        Function to provide access to criteria class (and provide caching of
-        class created)
-        Returns: A criteria instance
-        """
-        # if we don't have a cached _criteria class, create one and update the
-        # cache
-        if not self._criteria_cache:
-            self._criteria_cache = self._criteria(self._criteria_root)
-        # now return cached _criteria class
-        return self._criteria_cache
-
-    def open_database(self, db_file):
-        """
-        Sets self._db (opens database object) and errors if already set or file
-        does not yet exist
-        Args: A file path to an SQLite3 database
-        Raises: SystemExit if path does not exist,
-                AssertionError if self._db is already set
-        Returns: Nothing
-        """
-        if not os.path.exists(db_file):
-            raise SystemExit(_("Error:\tFile %s is not a valid database "
-                               "file") % db_file)
-        elif self._db is None:
-            self._db = AIdb.DB(db_file, commit=True)
-        else:
-            raise AssertionError('Opening database when already open!')
-
-    def get_database(self):
-        """
-        Returns self._db (database object) and errors if not set
-        Raises: AssertionError if self._db is not yet set
-        Returns: SQLite3 database object
-        """
-        if isinstance(self._db, AIdb.DB):
-            return(self._db)
-        else:
-            raise AssertionError('Database not yet open!')
-
-    database = property(get_database, open_database, None,
-                        "Holds database object for criteria database")
-
-    def get_service(self):
-        """
-        Returns self._service and errors if not yet set
-        Raises: AssertionError if self._service is not yet set
-        Returns: String object
-        """
-        if self._service is not None:
-            return(self._service)
-        else:
-            raise AssertionError('Service not yet set!')
-
-    def set_service(self, serv=None):
-        """
-        Sets self._service and errors if already set
-        Args: A string path to an AI service directory
-        Raises: SystemExit if path does not exist,
-                AssertionError if self._service is already set
-        Returns: Nothing
-        """
-        if not os.path.isdir(serv):
-            raise SystemExit(_("Error:\tDirectory %s is not a valid AI "
-                               "directory") % db_file)
-        elif self._service is None:
-            self._service = os.path.abspath(serv)
-        else:
-            raise AssertionError('Setting service when already set!')
-
-    service = property(get_service, set_service, None,
-                       "Holds path to service directory (i.e. /var/ai/46501)")
-
-    def find_SC_from_crit_man(self):
-        """
-        Find SC manifests as referenced in the criteria manifest
-        Preconditions: self._criteria_root is a valid XML DOM
-        Postconditions: self._smfDict will be a dictionary containing all
-                        SC manifest DOMs
-        Raises: SystemExit for XML processing errors
-                           for two SC manifests named the same
-                AssertionError if _critteria_root not set
-        Args: None
-        Returns: None
-        """
-        if self._criteria_root is None:
-            raise AssertionError(_("Error:\t _criteria_root not set!"))
-        try:
-            root = self._criteria_root.iterfind(".//sc_manifest_file")
-        except lxml.etree.LxmlError, e:
-            raise SystemExit(_("Error:\tCriteria manifest error:%s") % e)
-        # for each SC manifest file: get the URI and verify it, adding it to the
-        # dictionary of SMF SC manifests (this means we can support a criteria
-        # manifest with multiple SC manifests embedded or referenced)
-        for SC_man in root:
-            if SC_man.attrib['name'] in self._smfDict:
-                raise SystemExit(_("Error:\tTwo SC manfiests with name %s") %
-                                   SC_man.attrib['name'])
-            # if this is an absolute path just hand it off
-            if os.path.isabs(str(SC_man.attrib['URI'])):
-                self._smfDict[SC_man.attrib['name']] = \
-                    self.verify_SC_manifest(SC_man.attrib['URI'])
-            # this is not an absolute path - make it one
-            else:
-                self._smfDict[SC_man.attrib['name']] = \
-                    self.verify_SC_manifest(os.path.join(os.path.dirname(
-                                          self.criteria_path),
-                                          SC_man.attrib['URI']))
-        try:
-            root = self._criteria_root.iterfind(".//sc_embedded_manifest")
-        except lxml.etree.LxmlError, e:
-            raise SystemExit(_("Error:\tCriteria manifest error:%s") % e)
-        # for each SC manifest embedded: verify it, adding it to the
-        # dictionary of SMF SC manifests
-        for SC_man in root:
-            # strip the comments off the SC manifest
-            xml_data = lxml.etree.tostring(SC_man.getchildren()[0])
-            xml_data = xml_data.replace("<!-- ", "").replace(" -->", "")
-            xml_data = StringIO.StringIO(xml_data)
-            # parse and read in the SC manifest
-            self._smfDict[SC_man.attrib['name']] = \
-                self.verify_SC_manifest(xml_data, name=SC_man.attrib['name'])
-
-    def find_AI_from_criteria(self):
-        """
-        Find A/I manifest as referenced or embedded in criteria manifest
-        Preconditions: self._criteria_root is a valid XML DOM
-        Postconditions: self.manifest_path will be set if using a free-standing
-                        AI manifest otherwise self._AI_root will eb set to a
-                        valid XML DOM for the AI manifest
-        Raises: SystemExit for XML processing errors
-                           for no ai_manifest_file specification
-                AssertionError if _critteria_root not set
-        """
-        if self._criteria_root is None:
-            raise AssertionError(_("Error:\t_criteria_root not set!"))
-        try:
-            root = self._criteria_root.find(".//ai_manifest_file")
-        except lxml.etree.LxmlError, e:
-            raise SystemExit(_("Error:\tCriteria manifest error:%s") % e)
-        if not isinstance(root, lxml.etree._Element):
-            try:
-                root = self._criteria_root.find(".//ai_embedded_manifest")
-            except lxml.etree.LxmlError, e:
-                raise SystemExit(_("Error:\tCriteria manifest error:%s") % e)
-            if not isinstance(root, lxml.etree._Element):
-                raise SystemExit(_("Error:\tNo <ai_manifest_file> or " +
-                                   "<ai_embedded_manifest> element in "
-                                   "criteria manifest."))
-        try:
-            root.attrib['URI']
-        except KeyError:
-            self._AI_root = \
-                lxml.etree.tostring(root.find(".//ai_manifest"))
-            return
-        if os.path.isabs(root.attrib['URI']):
-            self.manifest_path = root.attrib['URI']
-        else:
-            # if we do not have an absolute path try using the criteria
-            # manifest's location for a base
-            self.manifest_path = \
-                os.path.join(os.path.dirname(self.criteria_path),
-                             root.attrib['URI'])
-    @property
-    def AI_schema(self):
-        """
-        Returns self._AIschema and errors if not yet set
-        Args: None
-        Raises: AssertionError if self._AIschema is not yet set
-        Returns: String object
-        """
-        if self._AIschema is not None:
-            return (self._AIschema)
-        else:
-            raise AssertionError('AIschema not set')
-
-    def set_AI_schema(self):
-        """
-        Sets self._AIschema and errors if imagepath not yet set.
-        Args: None
-        Raises: SystemExit if unable to find a valid AI schema
-        Returns: None
-        """
-        if os.path.exists(os.path.join(self.image_path,
-                                       IMG_AI_MANIFEST_SCHEMA)):
-            self._AIschema = os.path.join(self.image_path,
-                                          IMG_AI_MANIFEST_SCHEMA)
-        else:
-            if os.path.exists(SYS_AI_MANIFEST_SCHEMA):
-                self._AIschema = SYS_AI_MANIFEST_SCHEMA
-                print (_("Warning: Using A/I manifest schema <%s>\n") %
-                        self._AIschema)
-            else:
-                raise SystemExit(_("Error:\tUnable to find an A/I schema!"))
-
-    def get_image_path(self):
-        """
-        Returns self._imagepath and errors if not set
-        Raises: AssertionError if self._imagepath is not yet set
-        Returns: String object
-        """
-        if self._imagepath is not None:
-            return (self._imagepath)
-        else:
-            raise AssertionError('Imagepath not set')
-
-    def set_image_path(self, imagepath):
-        """
-        Sets self._imagepath but exits if already set or not a directory
-        Args: image path to a valid AI image
-        Raises: SystemExit if image path provided is not a directory
-                AssertionError if image path is already set
-        Returns: None
-        """
-        if not os.path.isdir(imagepath):
-            raise SystemExit(_("Error:\tInvalid imagepath " +
-                               "directory: %s") % imagepath)
-        if self._imagepath is None:
-            self._imagepath = os.path.abspath(imagepath)
-        else:
-            raise AssertionError('imagepath already set')
-
-    image_path = property(get_image_path, set_image_path, None,
-                        "Holds path to service's AI image")
-
-    def get_manifest_path(self):
-        """
-        Returns self._manifest and errors if not set
-        Raises: AssertionError if self._manifest is not yet set
-        Returns: String object path to AI manifest
-        """
-        if self._manifest is not None:
-            return(self._manifest)
-        else:
-            raise AssertionError('Manifest path not yet set!')
-
-    def set_manifest_path(self, mani=None):
-        """
-        Sets self._manifest and errors if already set
-        Args: path to an AI manifest
-        Raises: AssertionError if manifest is already set
-        Returns: None
-        """
-        if self._manifest is None:
-            self._manifest = os.path.abspath(mani)
-        else:
-            raise AssertionError('Setting manifest when already set!')
-
-    manifest_path = property(get_manifest_path, set_manifest_path, None,
-                             "Holds path to AI manifest being published")
-    @property
-    def manifest_name(self):
-        """
-        Returns: manifest name as defined in the A/I manifest (ensuring .xml is
-                 applied to the string)
-        Raises: SystemExit if <ai_manifest> tag can not be found
-        """
-        if self._AI_root.getroot().tag == "ai_manifest":
-            name = self._AI_root.getroot().attrib['name']
-        else:
-            raise SystemExit(_("Error:\tCan not find <ai_manifest> tag!"))
-        # everywhere we expect manifest names to be file names so ensure
-        # the name matches
-        if not name.endswith('.xml'):
-            name += ".xml"
-        return name
-
-    def verify_AI_manifest(self):
-        """
-        Used for verifying and loading AI manifest as defined by
-            DataFiles._AIschema.
-        Args: None.
-        Postconditions: Sets DataFiles._AI_root on success to a XML DOM
-        Raises: SystemExit on file open error or validation error.
-        """
-        try:
-            schema = file(self.AI_schema, 'r')
-        except IOError:
-            raise SystemExit(_("Error:\tCan not open: %s ") %
-                               self.AI_schema)
-        try:
-            xml_data = file(self.manifest_path, 'r')
-        except IOError:
-            raise SystemExit(_("Error:\tCan not open: %s ") %
-                               self.manifest_path)
-        except AssertionError:
-            # manifest path will be unset if we're not using a separate file for
-            # A/I manifest so we must emulate a file
-            xml_data = StringIO.StringIO(self._AI_root)
-        self._AI_root = verifyXML.verifyRelaxNGManifest(schema, xml_data)
-        if isinstance(self._AI_root, lxml.etree._LogEntry):
-            # catch if we area not using a manifest we can name with
-            # manifest_path
-            try:
-                raise SystemExit(_("Error:\tFile %s failed validation:\n\t%s") %
-                                 (os.path.basename(self.manifest_path),
-                                  self._AI_root.message))
-            # manifest_path will throw an AssertionError if it does not have
-            # a path use a different error message
-            except AssertionError:
-                raise SystemExit(_("Error:\tA/I manifest failed validation:"
-                                   "\n\t%s") % self._AI_root.message)
-
-
-    def verify_SC_manifest(self, data, name=None):
-        """
-        Used for verifying and loading SC manifest
-        Args:    data - file path, or StringIO object.
-                 name - Optionally, takes a name to provide error output,
-                        as a StringIO object will not have a file path to
-                        provide.
-        Returns: Provide an XML DOM for the SC manifest
-        Raises:  SystemExit on validation or file open error.
-        """
-        if not isinstance(data, StringIO.StringIO):
-            try:
-                data = file(data, 'r')
-            except IOError:
-                if name is None:
-                    raise SystemExit(_("Error:\tCan not open: %s") % data)
-                else:
-                    raise SystemExit(_("Error:\tCan not open: %s") % name)
-        xml_root = verifyXML.verifyDTDManifest(data, self.smfDtd)
-        if isinstance(xml_root, list):
-            if not isinstance(data, StringIO.StringIO):
-                print >> sys.stderr, (_("Error:\tFile %s failed validation:") %
-                                      data.name)
-            else:
-                print >> sys.stderr, (_("Error:\tSC Manifest %s failed "
-                                        "validation:") % name)
-            for err in xml_root:
-                print >> sys.stderr, err
-            raise SystemExit()
-        return(xml_root)
-
-    def verifyCriteria(self):
-        """
-        Used for verifying and loading criteria XML
-        Raises SystemExit:
-        *if the schema does not open
-        *if the XML file does not open
-        *if the XML is invalid according to the schema
-        Postconditions: self._criteria_root is a valid XML DOM of the criteria
-                        manifest and all MAC and IPv4 values are formatted
-                        according to verifyXML.prepValuesAndRanges()
-        """
-        try:
-            schema = file(self.criteriaSchema, 'r')
-        except IOError:
-            raise SystemExit(_("Error:\tCan not open: %s") %
-                             self.criteriaSchema)
-        try:
-            file(self.criteria_path, 'r')
-        except IOError:
-            raise SystemExit(_("Error:\tCan not open: %s") % self.criteria_path)
-        self._criteria_root = (verifyXML.verifyRelaxNGManifest(schema,
-                              self.criteria_path))
-        if isinstance(self._criteria_root, lxml.etree._LogEntry):
-            raise SystemExit(_("Error:\tFile %s failed validation:\n\t%s") %
-                             (self.criteria_path, self._criteria_root.message))
-        try:
-            verifyXML.prepValuesAndRanges(self._criteria_root,
-                                          self.database)
-        except ValueError, e:
-            raise SystemExit(_("Error:\tCriteria manifest error: %s") % e)
-
-
-if __name__ == '__main__':
-    gettext.install("ai", "/usr/lib/locale")
-
-    # check that we are root
-    if os.geteuid() != 0:
-        raise SystemExit(_("Error:\tNeed root privileges to execute"))
-
-    # load in all the options and file data
-    data = parse_options()
-
-    # if we have a default manifest do default manifest handling
-    if data.manifest_name == "default.xml":
-        do_default(data)
-
-    # if we have a non-default manifest first ensure it is a unique criteria
-    # set and then, if unique, add the manifest to the criteria database
-    else:
-        # if we have a None criteria from the criteria list then the manifest
-        # has no criteria which is illegal for a non-default manifest
-        if not data.criteria:
-            raise SystemExit(_("Error:\tNo criteria found " +
-                               "in non-default manifest -- "
-                               "at least one criterion needed!"))
-        find_colliding_manifests(data, find_colliding_criteria(data))
-        insert_SQL(data)
-
-    # move the manifest into place
-    place_manifest(data)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/ai-webserver/publish_manifest.py	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,1445 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+
+"""
+
+A/I Publish_Manifest
+
+"""
+
+import os.path
+import sys
+import StringIO
+import gettext
+import lxml.etree
+import hashlib
+from optparse import OptionParser
+
+import osol_install.auto_install.AI_database as AIdb
+import osol_install.auto_install.verifyXML as verifyXML
+import osol_install.libaiscf as smf
+
+INFINITY = str(0xFFFFFFFFFFFFFFFF)
+IMG_AI_MANIFEST_DTD = "auto_install/ai.dtd"
+SYS_AI_MANIFEST_DTD = "/usr/share/auto_install/ai.dtd"
+
+IMG_AI_MANIFEST_SCHEMA = "auto_install/ai_manifest.rng"
+
+def parse_options(cmd_options=None):
+    """
+    Parse and validate options
+    Args: Optional cmd_options, used for unit testing. Otherwise, cmd line
+          options handled by OptionParser
+    Returns: the DataFiles object populated and initialized
+    Raises: The DataFiles initialization of manifest(s) A/I, SC, SMF looks for
+            many error conditions and, when caught, are flagged to the user
+            via raising SystemExit exceptions.
+    """
+
+    usage = _("usage: %prog -n service_name -m AI_manifest"
+              " [-c <criteria=value|range> ... | -C criteria_file]")
+    parser = OptionParser(usage=usage, prog="add-manifest")
+    parser.add_option("-c", dest="criteria_c", action="append",
+                      default=[], help=_("Specify criteria: "
+                      "<-c criteria=value|range> ..."))
+    parser.add_option("-C",  dest="criteria_file",
+                      default=None, help=_("Specify name of criteria "
+                      "XML file."))
+    parser.add_option("-m",  dest="manifest_path",
+                      default=None, help=_("Specify name of manifest "
+                      "to set criteria for."))
+    parser.add_option("-n",  dest="service_name",
+                      default=None, help=_("Specify name of install "
+                      "service."))
+
+    # Get the parsed options using parse_args().  We know we don't have
+    # args, so we're just grabbing the first item of the tuple returned.
+    options, args = parser.parse_args(cmd_options)
+    if len(args):
+        parser.error(_("Unexpected arguments: %s" % args))
+
+    # options are:
+    #    -c  criteria=<value/range> ...
+    #    -C  XML file with criteria specified
+    #    -n  service name
+    #    -m  manifest path to work with
+
+    # check that we got the install service's name and
+    # an AI manifest
+    if options.manifest_path is None or options.service_name is None:
+        parser.error(_("Missing one or more required options."))
+
+    # check that we aren't mixing -c and -C
+    if (options.criteria_c and options.criteria_file):
+        parser.error(_("Options used are mutually exclusive."))
+
+    # if we have criteria from cmd line, convert into dictionary
+    criteria_dict = None
+    if options.criteria_c:
+        try:
+            criteria_dict = criteria_to_dict(options.criteria_c)
+        except ValueError as err:
+            parser.error(err)
+
+    if options.criteria_file:
+        if not os.path.exists(options.criteria_file):
+            parser.error(_("Unable to find criteria file: %s") % 
+                         options.criteria_file)
+
+    # get an AIservice object for requested service
+    try:
+        svc = smf.AIservice(smf.AISCF(FMRI="system/install/server"),
+                            options.service_name)
+    except KeyError:
+        parser.error(_("Failed to find service %s") % options.service_name)
+
+    # get the service's data directory path and imagepath
+    try:
+        image_path = svc['image_path']
+        # txt_record is of the form "aiwebserver=example:46503" so split
+	# on ":" and take the trailing portion for the port number
+        port = svc['txt_record'].rsplit(':')[-1]
+    except KeyError, err:
+        parser.error(_("SMF data for service %s is corrupt. Missing "
+                       "property: %s\n") % (options.service_name, err))
+    service_dir = os.path.abspath("/var/ai/" + port)
+
+    # check that the service and imagepath directories exist,
+    # and the AI.db, criteria_schema.rng and ai_manifest.rng files
+    # are present otherwise the service is misconfigured
+    if not (os.path.isdir(service_dir) and
+            os.path.exists(os.path.join(service_dir, "AI.db"))):
+        parser.error("Need a valid A/I service directory")
+
+
+    try:
+        files = DataFiles(service_dir=service_dir, image_path=image_path,
+                      database_path=os.path.join(service_dir, "AI.db"),
+                      manifest_file=options.manifest_path,
+                      criteria_dict=criteria_dict,
+                      criteria_file=options.criteria_file)
+    except (AssertionError, IOError, ValueError) as err:
+        raise SystemExit(err)
+    except (lxml.etree.LxmlError) as err:
+        raise SystemExit(_("Error:\tmanifest error: %s") % err)
+
+    return(files)
+
+def criteria_to_dict(criteria):
+    """
+    Convert criteria list into dictionary. This function is intended to be
+    called by a main function, or the options parser, so it can potentially
+    raise the SystemExit exception.
+    Args: criteria in list format: [ criteria=value, criteria=value, ... ]
+          where value can be a:  single value
+                                 range (<lower>-<upper>)
+    Returns: dictionary of criteria { criteria: value, criteria: value, ... }
+             with all keys and values in lower case
+    Raises: ValueError on malformed name=value strings in input list.
+    """
+    cri_dict = {}
+    for entry in criteria:
+        entries = entry.lower().partition("=")
+
+        if entries[1]:
+            if not entries[0]:
+                raise ValueError(_("Missing criteria name in "
+                                   "'%s'\n") % entry)
+            elif entries[0] in cri_dict:
+                raise ValueError(_("Duplicate criteria: '%s'\n") %
+                             entries[0])
+            elif not entries[2]:
+                raise ValueError(_("Missing value for criteria "
+                                   "'%s'\n") % entries[0])
+            cri_dict[entries[0]] = entries[2]
+        else:
+            raise ValueError(_("Criteria must be of the form "
+                               "<criteria>=<value>\n"))
+
+    return cri_dict
+
+def find_colliding_criteria(criteria, db, exclude_manifests=None):
+    """
+    Returns: A dictionary of colliding criteria with keys being manifest name
+             and instance tuples and values being the DB column names which
+             collided
+    Args:    criteria - Criteria object holding the criteria that is to be
+                        added/set for a manifest.
+             db - AI_database object for the install service.
+             exclude_manifests -A list of manifest names from DB to ignore.
+                                This arg is passed in when we're calling this
+                                function to find criteria collisions for an
+                                already published manifest.
+    Raises:  SystemExit if: criteria is not found in database
+                            value is not valid for type (integer and hexadecimal
+                            checks)
+                            range is improper
+    """
+    class Fields(object):
+        """
+        Define convenience indexes
+        """
+        # manifest name is row index 0
+        MANNAME = 0
+        # manifest instance is row index 1
+        MANINST = 1
+        # criteria is row index 2 (when a single valued criteria)
+        CRIT = 2
+        # minimum criteria is row index 2 (when a range valued criteria)
+        MINCRIT = 2
+        # maximum criteria is row index 3 (when a range valued criteria)
+        MAXCRIT = 3
+
+    # collisions is a dictionary to hold keys of the form (manifest name,
+    # instance) which will point to a comma-separated string of colliding
+    # criteria
+    collisions = dict()
+
+    # verify each range criteria in the manifest is well formed and collect
+    # collisions with database entries
+    for crit in criteria:
+        # gather this criteria's values from the manifest
+        man_criterion = criteria[crit]
+
+        # check "value" criteria here (check the criteria exists in DB, and
+        # then find collisions)
+        if isinstance(man_criterion, basestring):
+            # only check criteria in use in the DB
+            if crit not in AIdb.getCriteria(db.getQueue(),
+                                            onlyUsed=False, strip=False):
+                raise SystemExit(_("Error:\tCriteria %s is not a " +
+                                   "valid criteria!") % crit)
+
+            # get all values in the database for this criteria (and
+            # manifest/instance pairs for each value)
+            db_criteria = AIdb.getSpecificCriteria(
+                db.getQueue(), crit, 
+                provideManNameAndInstance=True,
+                excludeManifests=exclude_manifests)
+
+            # will iterate over a list of the form [manName, manInst, crit,
+            # None]
+            for row in db_criteria:
+                # check if the database and manifest values differ
+                if(str(row[Fields.CRIT]).lower() ==
+		   str(man_criterion).lower()):
+                    # record manifest name, instance and criteria name
+                    try:
+                        collisions[row[Fields.MANNAME],
+                                   row[Fields.MANINST]] += crit + ","
+                    except KeyError:
+                        collisions[row[Fields.MANNAME],
+                                   row[Fields.MANINST]] = crit + ","
+
+        # This is a range criteria.  (Check that ranges are valid, that
+        # "unbounded" gets set to 0/+inf, ensure the criteria exists
+        # in the DB, then look for collisions.)
+        else:
+            # check for a properly ordered range (with unbounded being 0 or
+            # Inf.) but ensure both are not unbounded
+            if(
+               # Check for a range of -inf to inf -- not a valid range
+               (man_criterion[0] == "unbounded" and
+                man_criterion[1] == "unbounded"
+               ) or
+               # Check min > max -- range order reversed
+               (
+                (man_criterion[0] != "unbounded" and
+                 man_criterion[1] != "unbounded"
+                ) and
+                (man_criterion[0] > man_criterion[1])
+               )
+              ):
+                raise SystemExit(_("Error:\tCriteria %s "
+                                   "is not a valid range (MIN > MAX) or "
+                                   "(MIN and MAX unbounded).") % crit)
+
+            # Clean-up NULL's and changed "unbounded"s to 0 and
+            # really large numbers in case this Python does
+            # not support IEEE754.  Note "unbounded"s are already
+            # converted to lower case during manifest processing.
+            if man_criterion[0] == "unbounded":
+                man_criterion[0] = "0"
+            if man_criterion[1] == "unbounded":
+                man_criterion[1] = INFINITY
+            if crit == "mac":
+                # convert hex mac address (w/o colons) to a number
+                try:
+                    man_criterion[0] = long(str(man_criterion[0]).upper(), 16)
+                    man_criterion[1] = long(str(man_criterion[1]).upper(), 16)
+                except ValueError:
+                    raise SystemExit(_("Error:\tCriteria %s "
+                                       "is not a valid hexadecimal value") %
+                                     crit)
+
+            else:
+                # this is a decimal value
+                try:
+                    man_criterion = [long(str(man_criterion[0]).upper()),
+                                     long(str(man_criterion[1]).upper())]
+                except ValueError:
+                    raise SystemExit(_("Error:\tCriteria %s "
+                                       "is not a valid integer value") % crit)
+
+            # check to see that this criteria exists in the database columns
+            if ('MIN' + crit not in AIdb.getCriteria(
+                db.getQueue(), onlyUsed=False, strip=False))\
+                and ('MAX' + crit not in AIdb.getCriteria(
+                db.getQueue(), onlyUsed=False, strip=False)):
+                    raise SystemExit(_("Error:\tCriteria %s is not a "
+                                       "valid criteria!") % crit)
+
+            db_criteria = AIdb.getSpecificCriteria(
+                db.getQueue(), 'MIN' + crit, 'MAX' + crit,
+                provideManNameAndInstance=True,
+                excludeManifests=exclude_manifests)
+
+            # will iterate over a list of the form [manName, manInst, mincrit,
+            # maxcrit]
+            for row in db_criteria:
+                # arbitrarily large number in case this Python does
+                # not support IEEE754
+                db_criterion = ["0", INFINITY]
+
+                # now populate in valid database values (i.e. non-NULL values)
+                if row[Fields.MINCRIT]:
+                    db_criterion[0] = row[Fields.MINCRIT]
+                if row[Fields.MAXCRIT]:
+                    db_criterion[1] = row[Fields.MAXCRIT]
+                if crit == "mac":
+                    # use a hexadecimal conversion
+                    db_criterion = [long(str(db_criterion[0]), 16),
+                                    long(str(db_criterion[1]), 16)]
+                else:
+                    # these are decimal numbers
+                    db_criterion = [long(str(db_criterion[0])),
+                                    long(str(db_criterion[1]))]
+
+                # these three criteria can determine if there's a range overlap
+                if((man_criterion[1] >= db_criterion[0] and
+                   db_criterion[1] >= man_criterion[0]) or
+                   man_criterion[0] == db_criterion[1]):
+                    # range overlap so record the collision
+                    try:
+                        collisions[row[Fields.MANNAME],
+                                   row[Fields.MANINST]] += "MIN" + crit + ","
+                        collisions[row[Fields.MANNAME],
+                                   row[Fields.MANINST]] += "MAX" + crit + ","
+                    except KeyError:
+                        collisions[row[Fields.MANNAME],
+                                   row[Fields.MANINST]] = "MIN" + crit + ","
+                        collisions[row[Fields.MANNAME],
+                                   row[Fields.MANINST]] += "MAX" + crit + ","
+    return collisions
+
+def find_colliding_manifests(criteria, db, collisions, append_manifest=None):
+    """
+    For each manifest/instance pair in collisions check that the manifest
+    criteria diverge (i.e. are not exactly the same) and that the ranges do not
+    collide for ranges.
+    Raises if: a range collides, or if the manifest has the same criteria as a
+    manifest already in the database (SystemExit raised)
+    Returns: Nothing
+    Args: criteria - Criteria object holding the criteria that is to be
+                     added/set for a manifest.
+          db - AI_database object for the install service.
+          collisions - a dictionary with collisions, as produced by
+                       find_colliding_criteria()
+          append_manifest - name of manifest we're appending criteria to.
+                            This arg is passed in when we're calling this
+                            function to find criteria collisions for an
+                            already published manifest that we're appending
+                            criteria to.
+    """
+
+    # If we're appending criteria to an already published manifest, get a
+    # dictionary of the criteria that's already published for that manifest.
+    if append_manifest is not None:
+        published_criteria = AIdb.getManifestCriteria(append_manifest, 0,
+                                                      db.getQueue(),
+                                                      humanOutput=True,
+                                                      onlyUsed=False)
+
+    # check every manifest in collisions to see if manifest collides (either
+    # identical criteria, or overlaping ranges)
+    for man_inst in collisions:
+        # get all criteria from this manifest/instance pair
+        db_criteria = AIdb.getManifestCriteria(man_inst[0],
+                                               man_inst[1],
+                                               db.getQueue(),
+                                               humanOutput=True,
+                                               onlyUsed=False)
+
+        # iterate over every criteria in the database
+        for crit in AIdb.getCriteria(db.getQueue(),
+                                     onlyUsed=False, strip=False):
+
+            # Get the criteria name (i.e. no MIN or MAX)
+            crit_name = crit.replace('MIN', '', 1).replace('MAX', '', 1)
+            # Set man_criterion to the key of the DB criteria or None
+            man_criterion = criteria[crit_name]
+
+            if man_criterion and crit.startswith('MIN'):
+                man_criterion = man_criterion[0]
+            elif man_criterion and crit.startswith('MAX'):
+                man_criterion = man_criterion[1]
+
+            # If man_criterion is still None, and if we're appending criteria
+            # to an already published manifest, look for criteria in the
+            # published set of criteria for the manifest we're appending to
+            # as well, because existing criteria might cause a collision,
+            # which we need to compare for.
+            if man_criterion is None and append_manifest is not None:
+                man_criterion = published_criteria[str(crit)]
+                # replace database NULL's with Python None
+                if man_criterion == '':
+                    man_criterion = None
+
+            # set the database criteria
+            if db_criteria[str(crit)] == '':
+                # replace database NULL's with a Python None
+                db_criterion = None
+            else:
+                db_criterion = db_criteria[str(crit)]
+
+            # Replace unbounded's in the criteria (i.e. 0/+inf)
+            # with a Python None.
+            if isinstance(man_criterion, basestring) and \
+               man_criterion == "unbounded":
+                man_criterion = None
+
+            # check to determine if this is a range collision by using
+            # collisions and if not are the manifests divergent
+
+            if((crit.startswith('MIN') and
+                collisions[man_inst].find(crit + ",") != -1) or
+               (crit.startswith('MAX') and
+                collisions[man_inst].find(crit + ",") != -1)
+              ):
+                if (str(db_criterion).lower() != str(man_criterion).lower()):
+                    raise SystemExit(_("Error:\tManifest has a range "
+                                       "collision with manifest:%s/%i"
+                                       "\n\tin criteria: %s!") %
+                                     (man_inst[0], man_inst[1],
+                                      crit.replace('MIN', '', 1).
+                                      replace('MAX', '', 1)))
+
+            # the range did not collide or this is a single value (if we
+            # differ we can break out knowing we diverge for this
+            # manifest/instance)
+            elif(str(db_criterion).lower() != str(man_criterion).lower()):
+                # manifests diverge (they don't collide)
+                break
+
+        # end of for loop and we never broke out (diverged)
+        else:
+            raise SystemExit(_("Error:\tManifest has same criteria as " +
+                               "manifest: %s/%i!") %
+                             (man_inst[0], man_inst[1]))
+
+def insert_SQL(files):
+    """
+    Ensures all data is properly sanitized and formatted, then inserts it into
+    the database
+    Args: None
+    Returns: None
+    """
+    query = "INSERT INTO manifests VALUES("
+
+    # add the manifest name to the query string
+    query += "'" + AIdb.sanitizeSQL(files.manifest_name) + "',"
+    # check to see if manifest name is alreay in database (affects instance
+    # number)
+    if AIdb.sanitizeSQL(files.manifest_name) in \
+        AIdb.getManNames(files.database.getQueue()):
+            # database already has this manifest name get the number of
+            # instances
+        instance = AIdb.numInstances(AIdb.sanitizeSQL(files.manifest_name),
+                                     files.database.getQueue())
+
+    # this a new manifest
+    else:
+        instance = 0
+
+    # actually add the instance to the query string
+    query += str(instance) + ","
+
+    # we need to fill in the criteria or NULLs for each criteria the database
+    # supports (so iterate over each criteria)
+    for crit in AIdb.getCriteria(files.database.getQueue(),
+                                 onlyUsed=False, strip=False):
+        # for range values trigger on the MAX criteria (skip the MIN's
+        # arbitrary as we handle rows in one pass)
+        if crit.startswith('MIN'):
+            continue
+
+        # get the values from the manifest
+        values = files.criteria[crit.replace('MAX', '', 1)]
+
+        # If the critera manifest didn't specify this criteria, fill in NULLs
+        if values is None:
+            # use the criteria name to determine if this is a range
+            if crit.startswith('MAX'):
+                query += "NULL,NULL,"
+            # this is a single value
+            else:
+                query += "NULL,"
+
+        # this is a single criteria (not a range)
+        elif isinstance(values, basestring):
+            # translate "unbounded" to a database NULL
+            if values == "unbounded":
+                query += "NULL,"
+            else:
+                # use lower case for text strings
+                query += "'" + AIdb.sanitizeSQL(str(values).lower()) + "',"
+
+        # else values is a range
+        else:
+            for value in values:
+                # translate "unbounded" to a database NULL
+                if value == "unbounded":
+                    query += "NULL,"
+                # we need to deal with mac addresses specially being
+                # hexadecimal
+                elif crit.endswith("mac"):
+                    # need to insert with hex operand x'<val>'
+                    # use an upper case string for hex values
+                    query += "x'" + AIdb.sanitizeSQL(str(value).upper())+"',"
+                else:
+                    query += AIdb.sanitizeSQL(str(value).upper()) + ","
+
+    # strip trailing comma and close parentheses
+    query = query[:-1] + ")"
+
+    # update the database
+    query = AIdb.DBrequest(query, commit=True)
+    files.database.getQueue().put(query)
+    query.waitAns()
+    # in case there's an error call the response function (which will print the
+    # error)
+    query.getResponse()
+
+def do_default(files):
+    """
+    Removes old default.xml after ensuring proper format of new manifest
+    (does not copy new manifest over -- see place_manifest)
+    Args: None
+    Returns: None
+    Raises if: Manifest has criteria, old manifest can not be removed (exits
+               with SystemExit)
+    """
+    # check to see if any criteria is present -- if so, it can not be a default
+    # manifest (as they do not have criteria)
+    if files.criteria:
+        raise SystemExit(_("Error:\tCan not use AI criteria in a default " +
+                           "manifest"))
+    # remove old manifest
+    try:
+        os.remove(os.path.join(files.get_service(), 'AI_data', 'default.xml'))
+    except IOError, ioerr:
+        raise SystemExit(_("Error:\tUnable to remove default.xml:\n\t%s") %
+                           ioerr)
+
+def place_manifest(files):
+    """
+    Compares src and dst manifests to ensure they are the same; if manifest
+    does not yet exist, copies new manifest into place and sets correct
+    permissions and ownership
+    Args: files - DataFiles object holding all of the relevant and verified
+                  information for the manifest we're publishing.
+    Returns: None
+    Raises if: src and dst manifests differ (in MD5 sum), unable to write dst
+               manifest (raises SystemExit -- no clean up of database performed)
+    """
+
+    manifest_path = os.path.join(files.get_service(), "AI_data",
+                                files.manifest_name)
+
+    if files.is_dtd:
+        root = files._AI_root
+    else:
+        root = files._criteria_root
+
+    # if the manifest already exists see if it is different from what was
+    # passed in. If so, warn the user that we're using the existing manifest
+    if os.path.exists(manifest_path):
+        old_manifest = open(manifest_path, "r")
+        existing_MD5 = hashlib.md5("".join(old_manifest.readlines())).digest()
+        old_manifest.close()
+        current_MD5 = hashlib.md5(lxml.etree.tostring(root,
+                                 pretty_print=True, encoding=unicode)).digest()
+        if existing_MD5 != current_MD5:
+            raise SystemExit(_("Error:\tNot copying manifest, source and "
+                               "current versions differ -- criteria in "
+                               "place."))
+
+    # the manifest does not yet exist so write it out
+    else:
+
+        # Remove all <ai_criteria> elements if they exist.
+        for tag in root.xpath('/ai_criteria_manifest/ai_criteria'):
+            tag.getparent().remove(tag)
+ 
+        try:
+            root.write(manifest_path, pretty_print=True)
+        except IOError as err:
+            raise SystemExit(_("Error:\tUnable to write to dest. "
+                               "manifest:\n\t%s") % err)
+
+    # change read and write for owner
+    os.chmod(manifest_path, 0600)
+    # change to user/group root (uid/gid 0)
+    os.chown(manifest_path, 0, 0)
+
+def verifyCriteria(schema, criteria_path, db, is_dtd=True):
+    """
+    Used for verifying and loading criteria XML from a Criteria manifest,
+    which can be a combined Criteria manifest (for backwards compatibility
+    in supporting older install services) or a criteria manifest with just
+    criteria.
+    Args:       schema - path to schema file for criteria manifest.
+                criteria_path - path to criteria XML manifest file to verify.
+                db - database object for install service
+                is_dtd - criteria file should contain criteria only, no
+                         AI or SC manifest info
+    Raises IOError:
+        *if the schema does not open
+        *if the XML file does not open
+    Raises ValueError:
+        *if the XML is invalid according to the schema or has a syntax error
+    Returns:    A valid XML DOM of the criteria manifest and all MAC and IPV4
+                values are formatted according to
+                verifyXML.prepValuesAndRanges().
+    """
+    schema = open(schema, 'r')
+
+    # Remove AI and SC elements from within the Criteria manifests if
+    # they exist.  We validate those separately later.
+    try:
+        crit = lxml.etree.parse(criteria_path)
+    except lxml.etree.XMLSyntaxError, err:
+        raise ValueError(_("Error: %s") % err.error_log.last_error)
+
+    ai_sc_list = list()
+    ai_sc_paths = (".//ai_manifest_file", ".//ai_embedded_manifest",
+                   ".//sc_manifest_file", ".//sc_embedded_manifest")
+    for path in ai_sc_paths:
+        elements = crit.iterfind(path)
+
+        for elem in elements:
+            if is_dtd:
+                raise ValueError(_("Error:\tCriteria file should not contain "
+                                   "AI or SC manifest tags: %s") %
+                                 criteria_path)
+            ai_sc_list.append(elem)
+            elem.getparent().remove(elem)
+
+    # Verify the remaing DOM, which should only contain criteria
+    root = (verifyXML.verifyRelaxNGManifest(schema,
+            StringIO.StringIO(lxml.etree.tostring(crit.getroot()))))
+
+    if isinstance(root, lxml.etree._LogEntry):
+        raise ValueError(_("Error:\tFile %s failed validation:\n\t%s") %
+                         (criteria_path, root.message))
+    try:
+        verifyXML.prepValuesAndRanges(root, db)
+    except ValueError, err:
+        raise ValueError(_("Error:\tCriteria manifest error: %s") % err)
+
+    # Reinsert AI and SC elements back into the _criteria_root DOM.
+    for ai_sc_element in ai_sc_list:
+        root.getroot().append(ai_sc_element)
+
+    return root
+
+def verifyCriteriaDict(schema, criteria_dict, db):
+    """
+    Used for verifying and loading criteria from a dictionary of criteria.
+    Args:       schema - path to schema file for criteria manifest.
+                criteria_dict - dictionary of criteria to verify, in the form
+                                of { criteria: value, criteria: value, ... }
+                db - database object for install service
+    Raises IOError:
+               * if the schema does not open
+           ValueError:
+                * if the criteria_dict dictionary is empty
+                * if the XML is invalid according to the schema
+           AssertionError:
+                * if a value in the dictionary is empty
+    Returns:    A valid XML DOM of the criteria and all MAC and IPV4 values
+                are formatted according to verifyXML.prepValuesAndRanges().
+    """
+    schema = open(schema, 'r')
+
+    if not criteria_dict:
+        raise ValueError("Error:\tCriteria dictionary empty: %s\n"
+                         % criteria_dict)
+
+    root = lxml.etree.Element("ai_criteria_manifest")
+
+    for name, value_or_range in criteria_dict.iteritems():
+
+        if value_or_range is None:
+            raise AssertionError(_("Error: Missing value for criteria "
+                                   "'%s'") % name)
+
+        crit = lxml.etree.SubElement(root, "ai_criteria")
+        crit.set("name", name)
+
+        # If criteria is a range, split on "-" and add to
+        # XML DOM as a range element.
+        if AIdb.isRangeCriteria(db.getQueue(), name):
+            # Split on "-"
+            range_value = value_or_range.split('-', 1)
+
+            # If there was only a single value, means user specified
+            # this range criteria as a single value, add it as a single
+            # value
+            if len(range_value) == 1:
+                value_elem = lxml.etree.SubElement(crit, "value")
+                value_elem.text = value_or_range
+            else:
+                range_elem = lxml.etree.SubElement(crit, "range")
+                range_elem.text = " ".join(range_value)
+        else:
+            value_elem = lxml.etree.SubElement(crit, "value")
+            value_elem.text = value_or_range
+
+    # Verify the generated criteria DOM
+    root = verifyXML.verifyRelaxNGManifest(schema,
+                        StringIO.StringIO(lxml.etree.tostring(root)))
+    if isinstance(root, lxml.etree._LogEntry):
+        raise ValueError(_("Error: Criteria failed validation:\n\t%s") %
+                           root.message)
+
+    try:
+        verifyXML.prepValuesAndRanges(root, db)
+    except ValueError, err:
+        raise ValueError(_("Error:\tCriteria error: %s") % err)
+
+    return root
+
+# The criteria class is a list object with an overloaded get_item method
+# to act like a dictionary, looking up values from an underlying XML DOM.
+class Criteria(list):
+    """
+    Wrap list class to provide lookups in the criteria file when requested
+    """
+    def __init__(self, criteria_root):
+        # store the criteria manifest DOM root
+        self._criteria_root = criteria_root
+        # call the _init_() for the list class with a generator provided by
+        # find_criteria() to populate this _criteria() instance.
+        super(Criteria, self).__init__(self.find_criteria())
+
+    def find_criteria(self):
+        """
+        Find criteria from the criteria DOM.
+        Returns: A generator providing all criteria name attributes from
+                 <ai_criteria> tags
+        """
+
+        if self._criteria_root is None:
+            return
+
+        root = self._criteria_root.findall(".//ai_criteria")
+
+        # actually find criteria
+        for tag in root:
+            for child in tag.getchildren():
+                if (child.tag == "range" or child.tag == "value") and \
+                    child.text is not None:
+                    # criteria names are lower case
+                    yield tag.attrib['name'].lower()
+                else:
+                    # should not happen according to schema
+                    raise AssertionError(_("Criteria contains no values"))
+
+    def get_criterion(self, criterion):
+        """
+        Return criterion out of the criteria DOM
+        Returns: A list for range criterion with a min and max entry
+                 A string for value criterion
+        """
+
+        if self._criteria_root is None:
+            return None
+
+        source = self._criteria_root
+        for tag in source.getiterator('ai_criteria'):
+            crit = tag.get('name')
+            # compare criteria name case-insensitive
+            if crit.lower() == criterion.lower():
+                for child in tag.getchildren():
+                    if child.tag == "range":
+                        # this is a range response (split on white space)
+                        return child.text.split()
+                    elif child.tag == "value":
+                        # this is a value response (strip white space)
+                        return child.text.strip()
+                    # should not happen according to schema
+                    elif child.text is None:
+                        raise AssertionError(_("Criteria contains no values"))
+        return None
+
+    """
+    Look up a requested criteria (akin to dictionary access) but for an
+    uninitialized key will not raise an exception but return None)
+    """
+    __getitem__ = get_criterion
+
+    # disable trying to update criteria
+    __setitem__ = None
+    __delitem__ = None
+
+
+class DataFiles(object):
+    """
+    Class to contain and work with data files necessary for program
+    """
+    # schema for validating an AI criteria manifest
+    criteriaSchema = "/usr/share/auto_install/criteria_schema.rng"
+    # DTD for validating an SMF SC manifest
+    smfDtd = "/usr/share/lib/xml/dtd/service_bundle.dtd.1"
+
+    def __init__(self, service_dir=None, image_path=None,
+                 database_path=None, manifest_file=None,
+                 criteria_dict=None, criteria_file=None):
+
+        """
+        Initialize DataFiles instance. All parameters optional, however, proper
+        setup order asurred, if all data provided upon instantiation.
+        """
+
+        #
+        # State variables
+        #################
+        #
+
+        # Variable to cache criteria class for criteria property
+        self._criteria_cache = None
+
+        # Holds path to AI manifest being published (may not be set if an
+        # embedded manifest)
+        self._manifest = None
+
+        self.criteria_dict = criteria_dict
+        self.criteria_file = criteria_file
+
+        # Flag to indicate we're operating with the newer AI DTD,
+        # or with the older AI rng schema
+        self.is_dtd = True
+
+        #
+        # File system path variables
+        ############################
+        #
+
+        # Check AI Criteria Schema exists
+        if not os.path.exists(self.criteriaSchema):
+            raise IOError(_("Error:\tUnable to find criteria_schema: " +
+                            "%s") % self.criteriaSchema)
+
+        # Check SC manifest SMF DTD exists
+        if not os.path.exists(self.smfDtd):
+            raise IOError(_("Error:\tUnable to find SMF system " +
+                            "configuration DTD: %s") % self.smfDtd)
+
+        # A/I Manifest Schema
+        self._AIschema = None
+
+        # Holds path to service directory (i.e. /var/ai/46501)
+        self._service = None
+        if service_dir:
+            self.service = service_dir
+
+        # Holds path to AI image
+        self._imagepath = None
+        if image_path:
+            self.image_path = image_path
+            # set the AI schema once image_path is set
+            self.set_AI_schema()
+
+        # Holds database object for criteria database
+        self._db = None
+        if database_path:
+            # Set Database Path and Open SQLite3 Object
+            self.database = database_path
+            # verify the database's table/column structure (or exit if errors)
+            self.database.verifyDBStructure()
+
+        # Holds DOM for criteria manifest
+        self._criteria_root = None
+
+        # Determine if we're operating with the newer AI DTD,
+        # or with the older AI rng schema
+        try:
+            lxml.etree.DTD(self.AI_schema)
+            self.is_dtd = True
+        except lxml.etree.DTDParseError:
+            try:
+                lxml.etree.RelaxNG(file=self.AI_schema)
+                self.is_dtd = False
+            except lxml.etree.RelaxNGParseError:
+                raise ValueError(_("Error: Unable to determine AI manifest "
+                                   "validation type.\n"))
+
+        # Verify the AI manifest to make sure its valid
+        if self.is_dtd:
+            self._manifest = manifest_file
+            self.verify_AI_manifest()
+
+            # Holds DOMs for SC manifests
+            self._smfDict = dict()
+
+            # Look for a SC manifests specified within the manifest file
+            # sets _smfDict DOMs
+            self.find_SC_from_manifest(self._AI_root, self.manifest_path)
+        else:
+            # Holds path for manifest file (if reference to AI manifest URI
+            # found inside the combined Criteria manifest)
+            self._manifest = None
+
+            self.criteria_path = manifest_file
+            self._criteria_root = verifyCriteria(self.criteriaSchema,
+                                                 self.criteria_path,
+                                                 self.database,
+                                                 is_dtd=self.is_dtd)
+
+            # Holds DOM for AI manifest
+            self._AI_root = None
+
+            # Since we were provided a combined criteria manifest, look for
+            # an A/I manifest specified by the criteria manifest
+            if self._criteria_root:
+
+                # This will set _manifest to be the AI manifest path (if a
+                # file), or set _AI_root to the correct location in the
+                # criteria DOM (if embedded), or exit (if unable to find an
+                # AI manifest)
+                self.find_AI_from_criteria()
+
+                # This will parse _manifest (if it was set from above), load
+                # it into an XML DOM and set _AI_root to it.  The _AI_root DOM
+                # will then be verified.  The function will exit on error.
+                self.verify_AI_manifest()
+
+                # Holds DOMs for SC manifests
+                self._smfDict = dict()
+
+                # Look for a SC manifests specified within the manifest file
+                # sets _smfDict DOMs
+                self.find_SC_from_manifest(self._criteria_root,
+                                           self.criteria_path)
+
+        # Process criteria from -c, or -C.  This will setup _criteria_root
+        # as a DOM and will overwrite the DOM from criteria found in a
+        # combined Criteria manifest.
+        if self.criteria_file:
+            self.criteria_path = self.criteria_file
+            root = verifyCriteria(self.criteriaSchema, self.criteria_path,
+                                  self.database, is_dtd=self.is_dtd)
+            # Set this criteria into _criteria_root
+            self.set_criteria_root(root)
+        elif self.criteria_dict:
+            root = verifyCriteriaDict(self.criteriaSchema, self.criteria_dict,
+                                      self.database)
+            # Set this criteria into _criteria_root
+            self.set_criteria_root(root)
+
+    @property
+    def criteria(self):
+        """
+        Function to provide access to criteria class (and provide caching of
+        class created)
+        Returns: A criteria instance
+        """
+        # if we don't have a cached _criteria class, create one and update the
+        # cache
+        if not self._criteria_cache:
+            self._criteria_cache = Criteria(self._criteria_root)
+        # now return cached _criteria class
+        return self._criteria_cache
+
+    def open_database(self, db_file):
+        """
+        Sets self._db (opens database object) and errors if already set or file
+        does not yet exist
+        Args: A file path to an SQLite3 database
+        Raises: SystemExit if path does not exist,
+                AssertionError if self._db is already set
+        Returns: Nothing
+        """
+        if not os.path.exists(db_file):
+            raise SystemExit(_("Error:\tFile %s is not a valid database "
+                               "file") % db_file)
+        elif self._db is None:
+            self._db = AIdb.DB(db_file, commit=True)
+        else:
+            raise AssertionError('Opening database when already open!')
+
+    def get_database(self):
+        """
+        Returns self._db (database object) and errors if not set
+        Raises: AssertionError if self._db is not yet set
+        Returns: SQLite3 database object
+        """
+        if isinstance(self._db, AIdb.DB):
+            return(self._db)
+        else:
+            raise AssertionError('Database not yet open!')
+
+    database = property(get_database, open_database, None,
+                        "Holds database object for criteria database")
+
+    def get_service(self):
+        """
+        Returns self._service and errors if not yet set
+        Raises: AssertionError if self._service is not yet set
+        Returns: String object
+        """
+        if self._service is not None:
+            return(self._service)
+        else:
+            raise AssertionError('Service not yet set!')
+
+    def set_service(self, serv=None):
+        """
+        Sets self._service and errors if already set
+        Args: A string path to an AI service directory
+        Raises: SystemExit if path does not exist,
+                AssertionError if self._service is already set
+        Returns: Nothing
+        """
+        if not os.path.isdir(serv):
+            raise SystemExit(_("Error:\tDirectory %s is not a valid AI "
+                               "directory") % serv)
+        elif self._service is None:
+            self._service = os.path.abspath(serv)
+        else:
+            raise AssertionError('Setting service when already set!')
+
+    service = property(get_service, set_service, None,
+                       "Holds path to service directory (i.e. /var/ai/46501)")
+
+    def find_SC_from_manifest(self, manifest_root, manifest_path):
+        """
+        Find SC manifests as referenced in the manifest file.  We search for
+        embedded SC manifests first, then do a subsequent search for SC file
+        references and expand them in-place in the manifest_root DOM, to be
+        embedded SC manifests.
+        Preconditions: None
+        Postconditions: self._smfDict will be a dictionary containing all
+                        SC manifest DOMs
+        Raises: AssertionError: - if manifest_root or manifest_path are not set
+                                - if two SC manifests are named the same
+        Args:   manifest_root - a valid XML DOM of the manifest from which
+                                will find SC manifests.
+                manifest_path - a path to the file from which manifest_root
+                        was created.
+        Returns: None
+        """
+        root = manifest_root.iterfind(".//sc_embedded_manifest")
+
+        # For each SC manifest embedded: verify it, adding it to the
+        # dictionary of SMF SC manifests
+        for SC_man in root:
+            # strip the comments off the SC manifest
+            xml_data = lxml.etree.tostring(SC_man.getchildren()[0])
+            xml_data = xml_data.replace("<!-- ", "").replace("-->", "")
+            xml_data = StringIO.StringIO(xml_data)
+            # parse and read in the SC manifest
+            self._smfDict[SC_man.attrib['name']] = \
+                self.verify_SC_manifest(xml_data, name=SC_man.attrib['name'])
+
+        root = manifest_root.iterfind(".//sc_manifest_file")
+
+        # For each SC manifest file: get the URI and verify it, adding it to
+        # the dictionary of SMF SC manifests (this means we can support a
+        # manifest file with multiple SC manifests embedded or referenced)
+        for SC_man in root:
+            if SC_man.attrib['name'] in self._smfDict:
+                raise AssertionError(_("Error:\tTwo SC manifests with name %s")
+                                     % SC_man.attrib['name'])
+            # if this is an absolute path just hand it off
+            if os.path.isabs(str(SC_man.attrib['URI'])):
+                self._smfDict[SC_man.attrib['name']] = \
+                    self.verify_SC_manifest(SC_man.attrib['URI'])
+            # this is not an absolute path - make it one
+            else:
+                self._smfDict[SC_man.attrib['name']] = \
+                    self.verify_SC_manifest(os.path.join(os.path.dirname(
+                                          manifest_path),
+                                          SC_man.attrib['URI']))
+
+            # Replace each SC manifest file element in the manifest root DOM
+            # with an embedded manifest, using the content from its referenced
+            # file, which has just been loaded into the _smfDict of SC DOMs.
+            old_sc = self._smfDict[SC_man.attrib['name']]
+            new_sc = lxml.etree.Element("sc_embedded_manifest")
+            new_sc.set("name", SC_man.get("name"))
+            new_sc.text = "\n\t"
+            new_sc.tail = "\n"
+            embedded_sc = lxml.etree.Comment(" <?xml version='%s'?>\n\t"%
+                        old_sc.docinfo.xml_version +
+                        lxml.etree.tostring(old_sc, pretty_print=True,
+                                encoding=unicode, xml_declaration=False))
+            embedded_sc.tail = "\n"
+
+            new_sc.append(embedded_sc)
+            SC_man.getparent().replace(SC_man, new_sc)
+
+
+    def find_AI_from_criteria(self):
+        """
+        Find AI manifest as referenced or embedded in a criteria manifest.
+        Preconditions: self._criteria_root is a valid XML DOM
+        Postconditions: self.manifest_path will be set if using a free-standing
+                        AI manifest otherwise self._AI_root will be set to a
+                        valid XML DOM for the AI manifest
+        Raises: ValueError for XML processing errors
+                           for no ai_manifest_file specification
+                AssertionError if _criteria_root not set
+        """
+        if self._criteria_root is None:
+            raise AssertionError(_("Error:\t_criteria_root not set!"))
+
+        # Try to find an embedded AI manifest.
+        root = self._criteria_root.find(".//ai_embedded_manifest")
+        if root is not None:
+            self._AI_root = root.find(".//ai_manifest")
+            if self._AI_root is not None:
+                return
+            else:
+                raise ValueError(_("Error: <ai_embedded_manifest> missing "
+                                       "<ai_manifest>"))
+
+        # Try to find an AI manifest file reference.
+        root = self._criteria_root.find(".//ai_manifest_file")
+
+        if root is not None:
+            try:
+                if os.path.isabs(root.attrib['URI']):
+                    self.manifest_path = root.attrib['URI']
+                else:
+                    # if we do not have an absolute path try using the criteria
+                    # manifest's location for a base
+                    self.manifest_path = \
+                        os.path.join(os.path.dirname(self.criteria_path),
+                                     root.attrib['URI'])
+                return
+            except KeyError:
+                raise ValueError(_("Error: <ai_manifest_file> missing URI"))
+
+        raise ValueError(_("Error: No <ai_manifest_file> or "
+                           "<ai_embedded_manifest> element in "
+                           "criteria manifest."))
+
+    @property
+    def AI_schema(self):
+        """
+        Returns self._AIschema and errors if not yet set
+        Args: None
+        Raises: AssertionError if self._AIschema is not yet set
+        Returns: String object
+        """
+        if self._AIschema is not None:
+            return (self._AIschema)
+        else:
+            raise AssertionError('AIschema not set')
+
+    def set_AI_schema(self):
+        """
+        Sets self._AIschema and errors if imagepath not yet set.
+        Args: None
+        Raises: SystemExit if unable to find a valid AI schema
+        Returns: None
+        """
+        if os.path.exists(os.path.join(self.image_path,
+                                       IMG_AI_MANIFEST_DTD)):
+            self._AIschema = os.path.join(self.image_path,
+                                          IMG_AI_MANIFEST_DTD)
+        elif os.path.exists(os.path.join(self.image_path,
+                                         IMG_AI_MANIFEST_SCHEMA)):
+            self._AIschema = os.path.join(self.image_path,
+                                         IMG_AI_MANIFEST_SCHEMA)
+        else:
+            if os.path.exists(SYS_AI_MANIFEST_DTD):
+                self._AIschema = SYS_AI_MANIFEST_DTD
+                print (_("Warning: Using AI manifest dtd <%s>\n") %
+                        self._AIschema)
+            else:
+                raise SystemExit(_("Error:\tUnable to find an AI dtd!"))
+
+    def get_image_path(self):
+        """
+        Returns self._imagepath and errors if not set
+        Raises: AssertionError if self._imagepath is not yet set
+        Returns: String object
+        """
+        if self._imagepath is not None:
+            return (self._imagepath)
+        else:
+            raise AssertionError('Imagepath not set')
+
+    def set_image_path(self, imagepath):
+        """
+        Sets self._imagepath but exits if already set or not a directory
+        Args: image path to a valid AI image
+        Raises: SystemExit if image path provided is not a directory
+                AssertionError if image path is already set
+        Returns: None
+        """
+        if not os.path.isdir(imagepath):
+            raise SystemExit(_("Error:\tInvalid imagepath " +
+                               "directory: %s") % imagepath)
+        if self._imagepath is None:
+            self._imagepath = os.path.abspath(imagepath)
+        else:
+            raise AssertionError('imagepath already set')
+
+    image_path = property(get_image_path, set_image_path, None,
+                        "Holds path to service's AI image")
+
+    def get_manifest_path(self):
+        """
+        Returns self._manifest and errors if not set
+        Raises: AssertionError if self._manifest is not yet set
+        Returns: String object path to AI manifest
+        """
+        if self._manifest is not None:
+            return(self._manifest)
+        else:
+            raise AssertionError('Manifest path not yet set!')
+
+    def set_manifest_path(self, mani=None):
+        """
+        Sets self._manifest and errors if already set
+        Args: path to an AI manifest
+        Raises: AssertionError if manifest is already set
+        Returns: None
+        """
+        if self._manifest is None:
+            self._manifest = os.path.abspath(mani)
+        else:
+            raise AssertionError('Setting manifest when already set!')
+
+    manifest_path = property(get_manifest_path, set_manifest_path, None,
+                             "Holds path to AI manifest being published")
+    @property
+    def manifest_name(self):
+        """
+        Returns: manifest name as defined in the A/I manifest (ensuring .xml is
+                 applied to the string)
+        Raises: SystemExit if <ai_manifest> tag can not be found
+        """
+        if self._AI_root.getroot().tag == "ai_manifest":
+            name = self._AI_root.getroot().attrib['name']
+        elif self._AI_root.getroot().tag == "auto_install":
+            try:
+                ai_instance = self._AI_root.find(".//ai_instance")
+            except lxml.etree.LxmlError, err:
+                raise SystemExit(_("Error:\tAI manifest error: %s") %err)
+
+            name = ai_instance.attrib['name']
+        else:
+            raise SystemExit(_("Error:\tCan not find <ai_manifest> tag!"))
+        # everywhere we expect manifest names to be file names so ensure
+        # the name matches
+        if not name.endswith('.xml'):
+            name += ".xml"
+        return name
+
+    def verify_AI_manifest(self):
+        """
+        Used for verifying and loading AI manifest as defined by
+            DataFiles._AIschema.
+        Args: None.
+        Preconditions:  Expects its is_dtd variable to be set to determine
+                        how to validate the AI manifest.
+        Postconditions: Sets _AI_root on success to a XML DOM of the AI
+                        manifest.
+        Raises: IOError on file open error.
+                ValueError on validation error.
+        """
+        schema = file(self.AI_schema, 'r')
+
+        try:
+            xml_data = file(self.manifest_path, 'r')
+        except AssertionError:
+            # manifest path will be unset if we're not using a separate file
+            # for A/I manifest so we must emulate a file
+            xml_data = StringIO.StringIO(lxml.etree.tostring(self._AI_root))
+
+        if self.is_dtd:
+            self._AI_root = verifyXML.verifyDTDManifest(xml_data,
+                                                        self.AI_schema)
+
+            if isinstance(self._AI_root, list):
+                err = '\n'.join(self._AI_root)
+                raise ValueError(_("Error: AI manifest failed validation:\n%s")
+                                 % err)
+
+
+        else:
+            self._AI_root = verifyXML.verifyRelaxNGManifest(schema, xml_data)
+
+            if isinstance(self._AI_root, lxml.etree._LogEntry):
+                # catch if we are not using a manifest we can name with
+                # manifest_path
+                try:
+                    # manifest_path is a property that may raise an
+                    # AssertionError
+                    man_path = self.manifest_path
+                    raise ValueError(_("Error:\tFile %s failed validation:"
+                                 "\n\t%s") %
+                                 (os.path.basename(man_path),
+                                  self._AI_root.message))
+                # manifest_path will throw an AssertionError if it does not
+                # have a path use a different error message
+                except AssertionError:
+                    raise ValueError(_("Error: AI manifest failed validation:"
+                                   "\n\t%s") % self._AI_root.message)
+
+            # Replace the <ai_manifest_file> element (if one exists) with an
+            # <ai_embedded_manifest> element, using content from its referenced
+            # file which was just loaded into the _AI_root XML DOM
+            ai_manifest_file = self._criteria_root.find(".//ai_manifest_file")
+
+            if ai_manifest_file is not None:
+                new_ai = lxml.etree.Element("ai_embedded_manifest")
+                # add newlines to separate ai_embedded_manifest 
+                # from children
+                new_ai.text = "\n\t"
+                new_ai.tail = "\n"
+                self._AI_root.getroot().tail = "\n"
+                new_ai.append(self._AI_root.getroot())
+
+                ai_manifest_file.getparent().replace(ai_manifest_file, new_ai)
+
+
+    def verify_SC_manifest(self, data, name=None):
+        """
+        Used for verifying and loading SC manifest
+        Args:    data - file path, or StringIO object.
+                 name - Optionally, takes a name to provide error output,
+                        as a StringIO object will not have a file path to
+                        provide.
+        Returns: Provide an XML DOM for the SC manifest
+        Raises:  SystemExit on validation or file open error.
+        """
+        if not isinstance(data, StringIO.StringIO):
+            try:
+                data = file(data, 'r')
+            except IOError:
+                if name is None:
+                    raise SystemExit(_("Error:\tCan not open: %s") % data)
+                else:
+                    raise SystemExit(_("Error:\tCan not open: %s") % name)
+        xml_root = verifyXML.verifyDTDManifest(data, self.smfDtd)
+        if isinstance(xml_root, list):
+            if not isinstance(data, StringIO.StringIO):
+                print >> sys.stderr, (_("Error:\tFile %s failed validation:") %
+                                      data.name)
+            else:
+                print >> sys.stderr, (_("Error:\tSC Manifest %s failed "
+                                        "validation:") % name)
+            for err in xml_root:
+                print >> sys.stderr, err
+            raise SystemExit()
+        return(xml_root)
+
+    def set_criteria_root(self, root=None):
+        """
+        Used to set _criteria_root DOM with the criteria from the passed
+        in root DOM.  If _criteria_root already exists, overwrite its
+        <ai_criteria> elements with the criteria found in root.  If
+        _criteria_root doesn't already exist, simply set the root DOM
+        passed in as _criteria_root.
+
+        Args: root - A DOM for a criteria manifest
+        Postconditions: _criteria_root will be set with a criteria manifest
+                        DOM, or have its <ai_criteria> elements replaced with
+                        the criteria from the root DOM passed in.
+        """
+
+        # If the _criteria_root is not yet set, set it to the root
+        # DOM passed in.
+        if self._criteria_root is None:
+            self._criteria_root = root
+
+        # Else _criteria_root already exists (because this is being
+        # called with an older install service where the manifest input
+        # is a combined Criteria manifest), use the criteria specified
+        # in 'root' to overwrite any criteria in _criteria_root.
+        else:
+            # Remove all <ai_criteria> elements if they exist.
+            removed_criteria = False
+            path = '/ai_criteria_manifest/ai_criteria'
+            for tag in self._criteria_root.xpath(path):
+                removed_criteria = True
+                tag.getparent().remove(tag)
+
+            # If we removed a criteria from _criteria_root, this means
+            # criteria was also specified in a combined Criteria manifest.
+            # Warn user that those will be ignored, and criteria specified 
+            # on the command line via -c or -C override those.
+            if removed_criteria:
+                print("Warning: criteria specified in multiple places.\n"
+                      "  Ignoring criteria from combined Criteria manifest "
+                      "file.\n")
+
+            # Append all criteria from the new criteria root.
+            ai_criteria = root.iterfind(".//ai_criteria")
+
+            self._criteria_root.getroot().extend(ai_criteria)
+
+if __name__ == '__main__':
+    gettext.install("ai", "/usr/lib/locale")
+
+    # check that we are root
+    if os.geteuid() != 0:
+        raise SystemExit(_("Error:\tNeed root privileges to execute"))
+
+    # load in all the options and file data
+    data = parse_options()
+
+    # if we have a default manifest do default manifest handling
+    if data.manifest_name == "default.xml":
+        do_default(data)
+
+    # if we have a non-default manifest first ensure it is a unique criteria
+    # set and then, if unique, add the manifest to the criteria database
+    else:
+        # if we have a None criteria from the criteria list then the manifest
+        # has no criteria which is illegal for a non-default manifest
+        if not data.criteria:
+            raise SystemExit(_("Error:\tAt least one criterion must be " +
+                               "provided with a non-default manifest."))
+        find_colliding_manifests(data.criteria, data.database,
+            find_colliding_criteria(data.criteria, data.database))
+        insert_SQL(data)
+
+    # move the manifest into place
+    place_manifest(data)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/ai-webserver/set_criteria.py	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,281 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+"""
+AI set-criteria
+"""
+
+import gettext
+import os.path
+import sys
+import lxml.etree
+from optparse import OptionParser
+
+import publish_manifest as pub_man
+import osol_install.auto_install.AI_database as AIdb
+import osol_install.libaiscf as smf
+
+def parse_options(cmd_options=None):
+    """
+    Parse and validate options
+    Args: Optional cmd_options, used for unit testing. Otherwise, cmd line
+          options handled by OptionParser
+    Returns: the DataFiles object populated and initialized
+    Raises: The DataFiles initialization of manifest(s) A/I, SC, SMF looks for
+            many error conditions and, when caught, are flagged to the user
+            via raising SystemExit exceptions.
+    """
+
+    usage = _("usage: %prog -n service_name -m AI_manifest_name"
+              " [-c|-a <criteria=value|range> ...] | -C criteria_file")
+
+    parser = OptionParser(usage=usage, prog="set-criteria")
+    parser.add_option("-a", dest="criteria_a", action="append",
+                      default=[], help=_("Specify criteria to append: "
+                      "<-a criteria=value|range> ..."))
+    parser.add_option("-c", dest="criteria_c", action="append",
+                      default=[], help=_("Specify criteria: "
+                      "<-c criteria=value|range> ..."))
+    parser.add_option("-C",  dest="criteria_file",
+                      default=None, help=_("Specify name of criteria "
+                      "XML file."))
+    parser.add_option("-m",  dest="manifest_name",
+                      default=None, help=_("Specify name of manifest "
+                      "to set criteria for."))
+    parser.add_option("-n",  dest="service_name",
+                      default=None, help=_("Specify name of install "
+                      "service."))
+
+    # Get the parsed options using parse_args().  We know we don't have
+    # args, so check to make sure there are none.
+    options, args = parser.parse_args(cmd_options)
+    if len(args):
+        parser.error(_("Unexpected arguments: %s" % args))
+
+    # Check that we have the install service's name and
+    # an AI manifest name
+    if options.service_name is None or options.manifest_name is None:
+        parser.error(_("Missing one or more required options."))
+
+    # check that we aren't mixing -a, -c, and -C
+    if (options.criteria_a and options.criteria_c) or \
+        (options.criteria_a and options.criteria_file) or \
+        (options.criteria_c and options.criteria_file):
+        parser.error(_("Options used are mutually exclusive."))
+
+    return options
+
+def check_published_manifest(service_dir, db, manifest_name):
+    """
+    Used for checking that a manifest is already published in the
+    install service specified.  Checks to make sure manifest
+    exists in the install service's DB, and that the manifest also
+    exists in the install service's published files area.
+    Args:
+          service_dir - config directory of install service to check.
+          db - db object of install service to check against.
+          manifest_name - name of manifest to check.
+    Postconditions: None
+    Returns: True if manifest exists in install service
+             False if manifest does not exist.
+    """
+
+    # Check if manifest exists in the service's criteria DB.
+    if AIdb.sanitizeSQL(manifest_name) not in AIdb.getManNames(db.getQueue()):
+        print(_("Error: install service does not contain the specified "
+                "manifest: %s") % manifest_name)
+        return False
+
+    # Check if manifest file exists in the service's published area.
+    published_path = os.path.join(service_dir, "AI_data", manifest_name)
+
+    if not os.path.exists(published_path):
+        print(_("Error: manifest missing from published area: %s") %
+                published_path)
+        return False
+
+    return True
+
+def format_value(crit, value):
+    """
+    Format's a value (for use with set_criteria()) based on its
+    criteria type.
+    Args: crit - the criteria name.
+          value - the value to format.
+    Returns:
+          Formatted value for (used by set_criteria()) to use in
+          a string to query the install service's DB.
+    """
+    # For the value "unbounded", we store this as "NULL" in the DB.
+    if value == "unbounded":
+        return "NULL"
+    else:
+        formatted_val = "'" + AIdb.sanitizeSQL(str(value).upper()) + "'"
+
+        # If its the "mac" criteria, must add a special hex operand
+        if crit == "mac":
+            return "x" + formatted_val
+
+        return formatted_val
+
+def set_criteria(criteria, manifest_name, db, append=False):
+    """
+    Set a manifest's record in the criteria database with the
+    criteria provided.
+    If append is True -- append ones that aren't already set for
+    the manifest, and replace ones that are.
+    if append is False -- completely remove all criteria already
+    set for the manifest, and use only the criteria specified.
+    """
+
+    # Build a list of criteria nvpairs to update
+    nvpairs = list()
+
+    # we need to fill in the criteria or NULLs for each criteria the database
+    # supports (so iterate over each criteria)
+    for crit in AIdb.getCriteria(db.getQueue(), onlyUsed=False, strip=True):
+
+        # Get the value from the manifest
+        values = criteria[crit]
+
+        # the critera manifest didn't specify this criteria
+        if values is None:
+            # If we not appending criteria, then we must write in NULLs
+            # for this criteria since we're removing all criteria not
+            # specified.
+            if not append:
+                # if the criteria we're processing is a range criteria, fill in
+                # NULL for two columns, MINcrit and MAXcrit
+                if AIdb.isRangeCriteria(db.getQueue(), crit):
+                    nvpairs.append("MIN" + crit + "=NULL")
+                    nvpairs.append("MAX" + crit + "=NULL")
+                # this is a single value
+                else:
+                    nvpairs.append(crit + "=NULL")
+
+        # this is a single criteria (not a range)
+        elif isinstance(values, basestring):
+            # translate "unbounded" to a database NULL
+            if values == "unbounded":
+                nvstr = crit + "=NULL"
+            else:
+                # use lower case for text strings
+                nvstr = crit + "='" + AIdb.sanitizeSQL(str(values).lower()) \
+                        + "'"
+            nvpairs.append(nvstr)
+
+        # Else the values are a list this is a range criteria
+        else:
+            # Set the MIN column for this range criteria
+            nvpairs.append("MIN" + crit + "=" + format_value(crit, values[0]))
+
+            # Set the MAX column for this range criteria
+            nvpairs.append("MAX" + crit + "=" + format_value(crit, values[1]))
+
+    query = "UPDATE manifests SET " + ",".join(nvpairs) + \
+            " WHERE name='" + manifest_name + "'"
+
+    # update the DB
+    query = AIdb.DBrequest(query, commit=True)
+    db.getQueue().put(query)
+    query.waitAns()
+    # in case there's an error call the response function (which
+    # will print the error)
+    query.getResponse()
+
+
+if __name__ == '__main__':
+    gettext.install("ai", "/usr/lib/locale")
+
+    options = parse_options()
+
+    # Get the SMF service object for the install service specified.
+    try:
+        svc = smf.AIservice(smf.AISCF(FMRI="system/install/server"),
+                            options.service_name)
+    except KeyError:
+        raise SystemExit(_("Error: Failed to find service %s") %
+                         options.service_name)
+
+    # Get the install service's data directory and database path
+    try:
+        port = svc['txt_record'].rsplit(':')[-1]
+    except KeyError:
+        raise SystemExit(_("SMF data for service %s is corrupt.\n") %
+                         options.service_name)
+    service_dir = os.path.abspath("/var/ai/" + port)
+    database = os.path.join(service_dir, "AI.db")
+
+    # Check that the service directory and database exist
+    if not (os.path.isdir(service_dir) and os.path.exists(database)):
+        raise SystemExit("Error: Invalid AI service directory: %s" %
+                         service_dir)
+
+    # Open the database
+    db = AIdb.DB(database, commit=True)
+
+    # Check to make sure that the manifest whose criteria we're
+    # updating exists in the install service.
+    if not check_published_manifest(service_dir, db, options.manifest_name):
+        raise SystemExit(1)
+
+    # Process and validate criteria from -a, -c, or -C, and store
+    # store the criteria in a Criteria object.
+    try:
+        if options.criteria_file:
+            root = pub_man.verifyCriteria(pub_man.DataFiles.criteriaSchema,
+                                          options.criteria_file, db)
+        elif options.criteria_a:
+            criteria_dict = pub_man.criteria_to_dict(options.criteria_a)
+            root = pub_man.verifyCriteriaDict(pub_man.DataFiles.criteriaSchema,
+                                              criteria_dict, db)
+        elif options.criteria_c:
+            criteria_dict = pub_man.criteria_to_dict(options.criteria_c)
+            root = pub_man.verifyCriteriaDict(pub_man.DataFiles.criteriaSchema,
+                                              criteria_dict, db)
+    except (AssertionError, IOError, ValueError) as err:
+        raise SystemExit(err)
+    except (lxml.etree.LxmlError) as err:
+        raise SystemExit(_("Error:\tmanifest error: %s") % err)
+
+    # Instantiate a Criteria object with the XML DOM of the criteria.
+    criteria = pub_man.Criteria(root)
+
+    # Ensure the criteria we're adding/setting for this manifest doesn't
+    # cause a criteria collision in the DB.
+    colliding_criteria = pub_man.find_colliding_criteria(criteria, db,
+                             exclude_manifests=[options.manifest_name])
+    # If we're appending criteria pass the manifest name
+    if options.criteria_a:
+        pub_man.find_colliding_manifests(criteria, db, colliding_criteria,
+                                         append_manifest=options.manifest_name)
+    else:
+        pub_man.find_colliding_manifests(criteria, db, colliding_criteria,
+                                         append_manifest=None)
+
+    # Update the criteria for this manifest.
+    if options.criteria_a:
+        set_criteria(criteria, options.manifest_name, db, append=True)
+    else:
+        set_criteria(criteria, options.manifest_name, db, append=False)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/ai-webserver/test/test_ai_database.py	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,204 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''
+To run these tests, see the instructions in usr/src/tools/tests/README.
+Remember that since the proto area is used for the PYTHONPATH, the gate
+must be rebuilt for these tests to pick up any changes in the tested code.
+
+'''
+
+import gettext
+import unittest
+import osol_install.auto_install.AI_database as AIdb
+
+gettext.install("ai-test")
+
+class MockDataBase(object):
+    '''Class for mock database '''
+    def __init__(self):
+        self.queue  = MockQueue()
+
+    def getQueue(self):
+        return self.queue
+
+class MockQueue(object):
+    '''Class for mock database '''
+    def __init__(self):
+        self.criteria = None
+
+    def put(self, query):
+        return
+
+class MockQuery(object):
+    '''Class for mock query '''
+    def __init__(self):
+        self.query = None
+
+    def __call__(self, query, commit=False):
+        self.query = query
+        return self
+
+    def waitAns(self):
+        return
+
+    def getResponse(self):
+        return
+
+class MockGetCriteria(object):
+    '''Class for mock getCriteria '''
+    def __init__(self):
+        self.crit_stripped = ["arch", "mem", "ipv4", "mac"]
+        self.crit_unstripped = ["MINmem", "MINipv4", "MINmac", 
+                                "MAXmem", "MAXipv4", "MAXmac", "arch"]
+
+    def __call__(self, queue, onlyUsed=False, strip=False):
+        if strip:
+            return self.crit_stripped
+        else:
+            return self.crit_unstripped
+
+class MockDataFiles(object):
+    '''Class for mock DataFiles'''
+    def __init__(self):
+        self.criteria = None
+        self.database = MockDataBase()
+
+class getSpecificCriteria(unittest.TestCase):
+    '''Tests for getSpecificCriteria'''
+
+    def setUp(self):
+        '''unit test set up
+
+        '''
+        self.aidb_DBrequest = AIdb.DBrequest
+        self.mockquery = MockQuery()
+        AIdb.DBrequest = self.mockquery
+        self.files = MockDataFiles()
+
+    def tearDown(self):
+        '''unit test tear down
+        Functions originally saved in setUp are restored to their
+        original values.
+        '''
+        AIdb.DBrequest = self.aidb_DBrequest
+
+    def test_arch_no_exclude(self):
+        '''Verify arch query string with no exclude option'''
+        criteria = "arch"
+        queue = self.files.database.getQueue()
+        AIdb.getSpecificCriteria(queue, criteria)
+        expect_query = "SELECT arch FROM manifests WHERE arch IS NOT NULL"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_arch_exclude(self):
+        '''Verify arch query string with exclude option'''
+        criteria = "arch"
+        queue = self.files.database.getQueue()
+        AIdb.getSpecificCriteria(queue, criteria, excludeManifests=["suexml"])
+        expect_query = "SELECT arch FROM manifests WHERE arch IS NOT NULL " + \
+                       "AND name IS NOT 'suexml'"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_MINipv4(self):
+        '''Verify single MIN query string '''
+        criteria = "MINipv4"
+        queue = self.files.database.getQueue()
+        AIdb.getSpecificCriteria(queue, criteria)
+        expect_query = "SELECT MINipv4 FROM manifests WHERE MINipv4 IS " + \
+                       "NOT NULL"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_MAXipv4(self):
+        '''Verify single MAX query string '''
+        criteria = "MAXipv4"
+        queue = self.files.database.getQueue()
+        AIdb.getSpecificCriteria(queue, criteria)
+        expect_query = "SELECT MAXipv4 FROM manifests WHERE MAXipv4 IS " + \
+                       "NOT NULL"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_MIN_MAXmem(self):
+        '''Verify mem range query string '''
+        criteria = "MINmem"
+        criteria2 = "MAXmem"
+        queue = self.files.database.getQueue()
+        AIdb.getSpecificCriteria(queue, criteria, criteria2=criteria2)
+        expect_query = "SELECT MINmem, MAXmem FROM manifests WHERE " + \
+                       "(MINmem IS NOT NULL OR MAXmem IS NOT NULL)"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_MIN_MAXmac(self):
+        '''Verify mac range query string '''
+        criteria = "MINmac"
+        criteria2 = "MAXmac"
+        queue = self.files.database.getQueue()
+        AIdb.getSpecificCriteria(queue, criteria, criteria2=criteria2)
+        expect_query = "SELECT HEX(MINmac), HEX(MAXmac) FROM manifests " + \
+                       "WHERE (MINmac IS NOT NULL OR MAXmac IS NOT NULL)"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+
+class isRangeCriteria(unittest.TestCase):
+    '''Tests for isRangeCriteria'''
+
+    def setUp(self):
+        '''unit test set up
+
+        '''
+        self.aidb_getCriteria = AIdb.getCriteria
+        self.mockgetCriteria = MockGetCriteria()
+        AIdb.getCriteria = self.mockgetCriteria
+        self.files = MockDataFiles()
+
+    def tearDown(self):
+        '''unit test tear down
+        Functions originally saved in setUp are restored to their
+        original values.
+        '''
+        AIdb.getCriteria = self.aidb_getCriteria
+
+    def test_arch_not_range(self):
+        '''Verify arch returns false for range'''
+        is_range_criteria = AIdb.isRangeCriteria(self.files.database, "arch")
+        self.assertFalse(is_range_criteria)
+
+    def test_ipv4_is_range(self):
+        '''Verify ipv4 returns true for range'''
+        is_range_criteria = AIdb.isRangeCriteria(self.files.database, "ipv4")
+        self.assertTrue(is_range_criteria)
+
+    def test_mac_is_range(self):
+        '''Verify mac returns true for range'''
+        is_range_criteria = AIdb.isRangeCriteria(self.files.database, "mac")
+        self.assertTrue(is_range_criteria)
+
+    def test_mem_is_range(self):
+        '''Verify mem returns true for range'''
+        is_range_criteria = AIdb.isRangeCriteria(self.files.database, "mem")
+        self.assertTrue(is_range_criteria)
+
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/ai-webserver/test/test_publish_manifest.py	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,246 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''
+To run these tests, see the instructions in usr/src/tools/tests/README.
+Remember that since the proto area is used for the PYTHONPATH, the gate
+must be rebuilt for these tests to pick up any changes in the tested code.
+
+'''
+
+import gettext
+import tempfile
+import unittest
+import publish_manifest as publish_manifest
+import osol_install.auto_install.AI_database as AIdb
+
+
+gettext.install("ai-test")
+
+class MockGetCriteria(object):
+    '''Class for mock getCriteria '''
+    def __init__(self):
+        self.crit_stripped = ["arch", "mem", "ipv4", "mac"]
+        self.crit_unstripped = ["MINmem", "MINipv4", "MINmac",
+                                "MAXmem", "MAXipv4", "MAXmac", "arch"]
+
+    def __call__(self, queue, onlyUsed=False, strip=False):
+        if strip:
+            return self.crit_stripped
+        else:
+            return self.crit_unstripped
+
+class MockDataFiles(object):
+    '''Class for mock DataFiles'''
+    def __init__(self):
+        self.criteria = None
+        self.database = MockDataBase()
+
+class MockQuery(object):
+    '''Class for mock query '''
+    def __init__(self):
+        self.query = None
+
+    def __call__(self, query, commit=False):
+        self.query = query
+        return self
+
+    def waitAns(self):
+        return
+
+    def getResponse(self):
+        return
+
+class MockQueue(object):
+    '''Class for mock database '''
+    def __init__(self):
+        self.criteria = None
+
+    def put(self, query):
+        return
+
+class MockDataBase(object):
+    '''Class for mock database '''
+    def __init__(self):
+        self.queue  = MockQueue()
+
+    def getQueue(self):
+        return self.queue
+
+class MockGetManifestCriteria(object):
+    '''Class for mock getCriteria '''
+    def __init__(self):
+        self.criteria = {"arch": "sparc", 
+                         "MINmem": None, "MAXmem": None, "MINipv4": None,
+                         "MAXipv4":None, "MINmac": None, "MAXmac": None}
+
+    def __call__(self, name, instance, queue, humanOutput=False, onlyUsed=True):
+        return self.criteria
+
+
+class ParseOptions(unittest.TestCase):
+    '''Tests for parse_options. Some tests correctly output usage msg'''
+
+    def test_parse_no_options(self):
+        '''Ensure no options caught'''
+        self.assertRaises(SystemExit, publish_manifest.parse_options, []) 
+        myargs = ["mysvc"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+        myargs = ["manifest"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+        myargs = ["mysvc", "manifest"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+
+    def test_parse_invalid_options(self):
+        '''Ensure invalid option flagged'''
+        myargs = ["-n", "mysvc", "-m", "manifest", "-u"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+
+    def test_parse_options_novalue(self):
+        '''Ensure options with missing value caught'''
+        myargs = ["-n", "mysvc", "-m", "manifest", "-c"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+        myargs = ["-n", "mysvc", "-m", "manifest", "-C"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+        myargs = ["-n", "-m", "manifest"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+        myargs = ["-n", "mysvc", "-m"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+
+    def test_parse_minusC_nosuchfile(self):
+        '''Ensure -C with no such file caught'''
+        myargs = ["-n", "mysvc", "-m", "manifest", "-C", tempfile.mktemp()] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+
+    def test_parse_mutually_exclusive(self):
+        '''Ensure mutually exclusive -c and -C options caught'''
+        myargs = ["-n", "mysvc", "-m", "manifest", "-c", "arch=i86pc", "-C",
+                  tempfile.mktemp()] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+
+    def test_parse_no_such_service(self):
+        '''Ensure no such service '''
+        myargs = ["-n", "mysvc", "-m", "manifest", "-c", "arch=i86pc"] 
+        self.assertRaises(SystemExit, publish_manifest.parse_options, myargs) 
+
+class CriteriaToDict(unittest.TestCase):
+    '''Tests for criteria_to_dict'''
+
+    def test_lower_case_conversion(self):
+        '''Ensure keys and values converted to lower case'''
+        criteria=['ARCH=SPARC']
+        cri_dict = publish_manifest.criteria_to_dict(criteria)
+        self.assertEquals(len(cri_dict), 1)
+        self.assertEquals(cri_dict['arch'], 'sparc')
+
+    def test_range_values(self):
+        '''Ensure ranges saved correctly'''
+        criteria=['mem=1048-2096']
+        cri_dict = publish_manifest.criteria_to_dict(criteria)
+        self.assertEquals(len(cri_dict), 1)
+        self.assertTrue(cri_dict['mem'], '1048-2096')
+
+    def test_multiple_entries(self):
+        '''Ensure multiple criteria handled correctly'''
+        criteria=['ARCH=i86pc', 'MEM=1024', 'IPV4=129.224.45.185',
+                  'PLATFORM=SUNW,Sun-Fire-T1000',
+                  'MAC=0:14:4F:20:53:94-0:14:4F:20:53:A0']
+        cri_dict = publish_manifest.criteria_to_dict(criteria)
+        self.assertEquals(len(cri_dict), 5)
+        self.assertTrue(cri_dict['arch'], 'i86pc')
+        self.assertTrue(cri_dict['mem'], '1024')
+        self.assertTrue(cri_dict['ipv4'], '129.224.45.185')
+        self.assertTrue(cri_dict['platform'], 'sunw,sun-fire-t1000')
+        self.assertTrue(cri_dict['mac'], '0:14:4f:20:53:94-0:14:4f:20:53:a0')
+
+    def test_dupicate_criteria_detected(self):
+        '''Ensure duplicate criteria are detected'''
+        criteria=['ARCH=SPARC', 'arch=i386']
+        self.assertRaises(ValueError, publish_manifest.criteria_to_dict,
+                          criteria)
+
+    def test_missing_equals(self):
+        '''Ensure missing equals sign is detected'''
+        criteria=['mem2048']
+        self.assertRaises(ValueError, publish_manifest.criteria_to_dict,
+                          criteria)
+
+    def test_missing_value(self):
+        '''Ensure missing value is detected'''
+        criteria=['arch=']
+        self.assertRaises(ValueError, publish_manifest.criteria_to_dict,
+                          criteria)
+
+    def test_missing_criteria(self):
+        '''Ensure missing criteria is detected'''
+        criteria=['=i386pc']
+        self.assertRaises(ValueError, publish_manifest.criteria_to_dict,
+                          criteria)
+
+    def test_no_criteria(self):
+        '''Ensure case of no criteria is handled'''
+        criteria=[]
+        cri_dict = publish_manifest.criteria_to_dict(criteria)
+        self.assertEquals(len(cri_dict), 0)
+        self.assertTrue(isinstance(cri_dict, dict))
+
+class FindCollidingManifests(unittest.TestCase):
+    '''Tests for find_colliding_manifests'''
+
+    def setUp(self):
+        '''unit test set up
+
+        '''
+        self.aidb_DBrequest = AIdb.DBrequest
+        self.aidb_getCriteria = AIdb.getCriteria
+        self.aidb_getManifestCriteria = AIdb.getManifestCriteria
+        self.mockquery = MockQuery()
+        self.mockgetCriteria = MockGetCriteria()
+        self.mockgetManifestCriteria = MockGetManifestCriteria()
+        AIdb.DBrequest = self.mockquery
+        AIdb.getCriteria = self.mockgetCriteria
+        AIdb.getManifestCriteria = self.mockgetManifestCriteria
+        self.files = MockDataFiles()
+
+    def tearDown(self):
+        '''unit test tear down
+        Functions originally saved in setUp are restored to their
+        original values.
+        '''
+        AIdb.DBrequest = self.aidb_DBrequest
+        AIdb.getCriteria = self.aidb_getCriteria
+        AIdb.getManifestCriteria = self.aidb_getManifestCriteria
+
+
+    def test_find_colliding_with_append(self):
+        '''Ensure collsions found with append'''
+        criteria={'arch': 'sparc', 'mem': None, 'ipv4': None, 'mac': None}
+        collisions = {(u'nosuchmanifest.xml', 0): 'MINipv4,MAXipv4,'}
+        self.assertRaises(SystemExit,
+                          publish_manifest.find_colliding_manifests,
+                          criteria, self.files.database, collisions,
+                          append_manifest="appendmanifest")
+
+if __name__ == '__main__':
+    unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/ai-webserver/test/test_set_criteria.py	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,317 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''
+To run these tests, see the instructions in usr/src/tools/tests/README.
+Remember that since the proto area is used for the PYTHONPATH, the gate
+must be rebuilt for these tests to pick up any changes in the tested code.
+
+'''
+
+import gettext
+import tempfile
+import unittest
+import set_criteria as set_criteria
+import osol_install.auto_install.AI_database as AIdb
+
+
+gettext.install("ai-test")
+
+class MockDataBase(object):
+    '''Class for mock database '''
+    def __init__(self):
+        self.queue  = MockQueue()
+
+    def getQueue(self):
+        return self.queue
+
+class MockQueue(object):
+    '''Class for mock database '''
+    def __init__(self):
+        self.criteria = None
+
+    def put(self, query):
+        return
+
+class MockQuery(object):
+    '''Class for mock query '''
+    def __init__(self):
+        self.query = None
+
+    def __call__(self, query, commit=False):
+        self.query = query
+        return self
+
+    def waitAns(self):
+        return
+
+    def getResponse(self):
+        return
+
+class MockGetCriteria(object):
+    '''Class for mock getCriteria '''
+    def __init__(self):
+        self.crit_stripped = ["arch", "mem", "ipv4", "mac"]
+        self.crit_unstripped = ["MINmem", "MINipv4", "MINmac",
+                                "MAXmem", "MAXipv4", "MAXmac", "arch"]
+
+    def __call__(self, queue, onlyUsed=False, strip=False):
+        if strip:
+            return self.crit_stripped
+        else:
+            return self.crit_unstripped
+
+class MockisRangeCriteria(object):
+    '''Class for mock isRangeCriteria '''
+    def __init__(self):
+        self.range = ["mem", "ipv4", "mac"]
+
+    def __call__(self, queue, crit):
+        if crit in self.range:
+            return True
+        return False
+
+class MockgetManNames(object):
+    '''Class for mock getManNames '''
+    def __init__(self):
+        self.man_names = ["fakeman"]
+
+    def __call__(self, queue):
+        return self.man_names
+
+class MockDataFiles(object):
+    '''Class for mock DataFiles'''
+    def __init__(self):
+        self.criteria = None
+        self.database = MockDataBase()
+
+class SetCriteria(unittest.TestCase):
+    '''Tests for set_criteria'''
+
+    def setUp(self):
+        '''unit test set up
+
+        '''
+        self.aidb_DBrequest = AIdb.DBrequest
+        self.aidb_isRangeCriteria = AIdb.isRangeCriteria
+        self.aidb_getCriteria = AIdb.getCriteria
+        self.mockquery = MockQuery()
+        self.mockgetCriteria = MockGetCriteria()
+        self.mockisRangeCriteria = MockisRangeCriteria()
+        AIdb.DBrequest = self.mockquery
+        AIdb.getCriteria = self.mockgetCriteria
+        AIdb.isRangeCriteria = self.mockisRangeCriteria
+        self.files = MockDataFiles()
+
+    def tearDown(self):
+        '''unit test tear down
+        Functions originally saved in setUp are restored to their
+        original values.
+        '''
+        AIdb.DBrequest = self.aidb_DBrequest
+        AIdb.getCriteria = self.aidb_getCriteria
+        AIdb.isRangeCriteria = self.aidb_isRangeCriteria
+
+    def test_unbounded_min(self):
+        '''Ensure set_criteria min query constructed properly '''
+        criteria = {"arch": "i86pc", "mem": ["unbounded", 4096]}
+        criteria.setdefault("ipv4")
+        criteria.setdefault("mac")
+        set_criteria.set_criteria(criteria, "myxml", self.files.database)
+        expect_query = "UPDATE manifests SET arch='i86pc',MINmem=NULL," + \
+                       "MAXmem='4096',MINipv4=NULL,MAXipv4=NULL,MINmac=NULL," +\
+                       "MAXmac=NULL WHERE name='myxml'"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_unbounded_max(self):
+        '''Ensure set_criteria max query constructed properly '''
+        criteria = {"arch": "i86pc", "mem": [1024, "unbounded"]}
+        criteria.setdefault("ipv4")
+        criteria.setdefault("mac")
+        set_criteria.set_criteria(criteria, "myxml", self.files.database)
+        expect_query = "UPDATE manifests SET arch='i86pc',MINmem='1024'," + \
+                       "MAXmem=NULL,MINipv4=NULL,MAXipv4=NULL,MINmac=NULL," + \
+                       "MAXmac=NULL WHERE name='myxml'"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_range(self):
+        '''Ensure set_criteria max query constructed properly '''
+        criteria = {"arch": "i86pc", "ipv4": ["10.0.30.100", "10.0.50.400"]}
+        criteria.setdefault("mac")
+        criteria.setdefault("mem")
+        set_criteria.set_criteria(criteria, "myxml", self.files.database)
+        expect_query = "UPDATE manifests SET arch='i86pc',MINmem=NULL," + \
+                       "MAXmem=NULL,MINipv4='10.0.30.100'," + \
+                       "MAXipv4='10.0.50.400',MINmac=NULL,MAXmac=NULL " + \
+                       "WHERE name='myxml'"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_append_unbounded_min(self):
+        '''Ensure set_criteria append min query constructed properly '''
+        criteria = {"arch": "i86pc", "mem": ["unbounded", 4096]}
+        criteria.setdefault("ipv4")
+        criteria.setdefault("mac")
+        set_criteria.set_criteria(criteria, "myxml", self.files.database,
+                                  append=True)
+        expect_query = "UPDATE manifests SET arch='i86pc',MINmem=NULL," \
+                       "MAXmem='4096' WHERE name='myxml'"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_append_unbounded_max(self):
+        '''Ensure set_criteria append max query constructed properly '''
+        criteria = {"arch": "i86pc", "mem": [2048, "unbounded"]}
+        criteria.setdefault("ipv4")
+        criteria.setdefault("mac")
+        set_criteria.set_criteria(criteria, "myxml", self.files.database,
+                                  append=True)
+        expect_query = "UPDATE manifests SET arch='i86pc',MINmem='2048'," \
+                       "MAXmem=NULL WHERE name='myxml'"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+    def test_append_range(self):
+        '''Ensure set_criteria append range query constructed properly '''
+        criteria = {"arch": "i86pc", "ipv4": ["10.0.10.10", "10.0.10.300"]}
+        criteria.setdefault("mem")
+        criteria.setdefault("mac")
+        set_criteria.set_criteria(criteria, "myxml", self.files.database,
+                                  append=True)
+        expect_query = "UPDATE manifests SET arch='i86pc',MINipv4=" + \
+                       "'10.0.10.10',MAXipv4='10.0.10.300' WHERE name='myxml'"
+        self.assertEquals(expect_query, self.mockquery.query)
+
+class CheckPublishedManifest(unittest.TestCase):
+    '''Tests for check_published_manifest'''
+
+    def setUp(self):
+        '''unit test set up
+
+        '''
+        self.aidb_DBrequest = AIdb.DBrequest
+        self.mockquery = MockQuery()
+        AIdb.DBrequest = self.mockquery
+        self.aidb_getManNames = AIdb.getManNames
+        self.mockgetManNames = MockgetManNames()
+        AIdb.getManNames = self.mockgetManNames
+        self.files = MockDataFiles()
+
+    def tearDown(self):
+        '''unit test tear down
+        Functions originally saved in setUp are restored to their
+        original values.
+        '''
+        AIdb.DBrequest = self.aidb_DBrequest
+        AIdb.getManNames = self.aidb_getManNames
+
+    def test_no_such_name(self):
+        '''Check no such manifest name caught '''
+        self.assertFalse(set_criteria.check_published_manifest("/tmp",
+                         self.files.database, "no_such_manifest"))
+
+    def test_manifest_not_published(self):
+        '''Check manifest not in published area caught'''
+        self.assertFalse(set_criteria.check_published_manifest("/tmp",
+                         self.files.database,
+                         self.mockgetManNames.man_names[0])) 
+
+class ParseOptions(unittest.TestCase):
+    '''Tests for parse_options. Some tests correctly output usage msg'''
+
+    def test_parse_no_options(self):
+        '''Ensure no options caught'''
+        self.assertRaises(SystemExit, set_criteria.parse_options, []) 
+        myargs = ["mysvc"] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+        myargs = ["manifest"] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+        myargs = ["mysvc", "manifest"] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+
+    def test_parse_invalid_options(self):
+        '''Ensure invalid option flagged'''
+        myargs = ["-n", "mysvc", "-m", "manifest", "-u"] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+
+    def test_parse_options_novalue(self):
+        '''Ensure options with missing value caught'''
+        myargs = ["-n", "mysvc", "-m", "manifest", "-c"] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+        myargs = ["-n", "mysvc", "-m", "manifest", "-C"] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+        myargs = ["-n", "mysvc", "-m", "manifest", "-a"] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+        myargs = ["-n", "-m", "manifest"]
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs)
+        myargs = ["-n", "mysvc", "-m"]
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs)
+
+    def test_parse_mutually_exclusive(self):
+        '''Ensure mutually exclusive options caught'''
+        # Ensure -C and -a caught
+        myargs = ["-n", "mysvc", "-m", "manifest", "-a", "arch=i86pc",
+                  "-C", tempfile.mktemp()] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+
+        # Ensure -C and -c caught
+        myargs = ["-n", "mysvc", "-m", "manifest", "-c", "arch=i86pc", "-C",
+                  tempfile.mktemp()] 
+        self.assertRaises(SystemExit, set_criteria.parse_options, myargs) 
+
+    def test_parse_valid_options(self):
+        '''Ensure valid options parse successfully'''
+        myargs = ["-n", "mysvc", "-m", "manifest", "-a", "arch=i86pc"]
+        exp_options = {'criteria_a': ['arch=i86pc'], 'service_name': 'mysvc',
+                       'manifest_name': 'manifest',
+                       'criteria_file': None, 'criteria_c': []}
+        options = set_criteria.parse_options(myargs)
+        self.assertEquals(exp_options['criteria_a'], options.criteria_a) 
+        self.assertEquals(exp_options['criteria_c'], options.criteria_c) 
+        self.assertEquals(exp_options['service_name'], options.service_name)
+        self.assertEquals(exp_options['manifest_name'], options.manifest_name) 
+        self.assertEquals(exp_options['criteria_file'], options.criteria_file)
+
+        myargs = ["-n", "mysvc", "-m", "manifest", "-c", "arch=i86pc"]
+        exp_options = {'criteria_a': [],
+                       'service_name': 'mysvc', 'manifest_name': 'manifest',
+                       'criteria_file': None, 'criteria_c': ['arch=i86pc']}
+        options = set_criteria.parse_options(myargs)
+        self.assertEquals(exp_options['criteria_a'], options.criteria_a) 
+        self.assertEquals(exp_options['criteria_c'], options.criteria_c) 
+        self.assertEquals(exp_options['service_name'], options.service_name)
+        self.assertEquals(exp_options['manifest_name'], options.manifest_name) 
+        self.assertEquals(exp_options['criteria_file'], options.criteria_file)
+
+        tempname = tempfile.mktemp()
+        myargs = ["-n", "mysvc", "-m", "manifest", "-C", tempname]
+        exp_options = {'criteria_a': [], 'service_name': 'mysvc',
+                       'manifest_name': 'manifest', 'criteria_file': tempname,
+                       'criteria_c': []}
+        options = set_criteria.parse_options(myargs)
+        self.assertEquals(exp_options['criteria_a'], options.criteria_a) 
+        self.assertEquals(exp_options['criteria_c'], options.criteria_c) 
+        self.assertEquals(exp_options['service_name'], options.service_name)
+        self.assertEquals(exp_options['manifest_name'], options.manifest_name) 
+        self.assertEquals(exp_options['criteria_file'], options.criteria_file)
+
+if __name__ == '__main__':
+    unittest.main()
--- a/usr/src/cmd/ai-webserver/verifyXML.py	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/ai-webserver/verifyXML.py	Fri Aug 20 11:31:18 2010 -0600
@@ -18,8 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 """
 
 A/I Verify Manifest
@@ -42,19 +41,19 @@
     # Explanation of options: Here we don't want to load the DTD since it is
     # passed in; don't use the network in case someone passes in an HTTP
     # reference in the XML as that could be unexpected; we do DTD validation
-    # separate from the XML file's validation; we want to strip comments out
-    # for now, since this processes SC manifests which are stored as a
-    # comment in some places
+    # separate from the XML file's validation; we want to leave comments
+    # for now, since the embedded SC manifest can be stored as a comment
+    # in some places
     parser = lxml.etree.XMLParser(load_dtd = False, no_network=True,
-                                  dtd_validation=False, remove_comments=True)
+                                  dtd_validation=False, remove_comments=False)
     dtd = lxml.etree.DTD(os.path.abspath(xml_dtd))
     try:
         root = lxml.etree.parse(data, parser)
     except IOError:
         raise SystemExit(_("Error:\tCan not open: %s" % data))
-    except lxml.etree.XMLSyntaxError, e:
-        for err in e.error_log:
-            result.append(err.message)
+    except lxml.etree.XMLSyntaxError, err:
+        for error in err.error_log:
+            result.append(error.message)
         return result
     if dtd.validate(root):
         return root
@@ -78,8 +77,8 @@
         root = lxml.etree.parse(data)
     except IOError:
         raise SystemExit(_("Error:\tCan not open: %s" % data))
-    except lxml.etree.XMLSyntaxError, e:
-        return e.error_log.last_error
+    except lxml.etree.XMLSyntaxError, err:
+        return err.error_log.last_error
     if relaxng.validate(root):
         return root
     return relaxng.error_log.last_error
@@ -190,7 +189,7 @@
     for crit_name in AIdb.getCriteria(database.getQueue(),
         onlyUsed = False, strip = False):
         if (crit_name.startswith("MIN")):
-            range_crit.append(crit_name.replace("MIN","",1))
+            range_crit.append(crit_name.replace("MIN", "", 1))
 
     # Loop through all criteria elements.
     for crit in criteriaRoot.findall('.//ai_criteria'):
@@ -250,12 +249,10 @@
                 # Handle IPv4 addressses.
                 elif (crit_name == "ipv4"):
                     new_values += __checkIPv4(one_value)
-                    ipv4_found = True
 
                 # Handle MAC addresses.
                 elif (crit_name == "mac"):
                     new_values += __checkMAC(one_value)
-                    mac_found = True
 
                 # Handle everything else by passing through.
                 else:
--- a/usr/src/cmd/auto-install/Makefile	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/Makefile	Fri Aug 20 11:31:18 2010 -0600
@@ -48,9 +48,11 @@
 OBJS =	${SRCS:%.c=${ARCH}/%.o}
 
 MANIFEST_FILES = ai_manifest.xml \
-		 ai_manifest.defval.xml \
-		 ai_manifest.rng \
-		 default.xml
+		 default.xml \
+		 ai.dtd \
+		 target.dtd \
+		 configuration.dtd \
+		 software.dtd
 
 SC_PROFILE_FILES = static_network.xml
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/ai.dtd	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,58 @@
+<!--
+ 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 auto_install (ai_instance)>
+
+<!ENTITY % target SYSTEM "target.dtd">
+%target;
+
+<!ENTITY % configuration SYSTEM "configuration.dtd">
+%configuration;
+
+<!ENTITY % software SYSTEM "software.dtd">
+%software;
+
+<!--
+	The source element, if specified, is for post installation changes
+	to the publisher for IPS. The first source element specified
+	will be considered the primary source, all additional sources
+	will be added as secondary sources.
+-->
+
+<!ELEMENT sc_manifest_file EMPTY>
+<!ATTLIST sc_manifest_file name CDATA #REQUIRED>
+<!ATTLIST sc_manifest_file URI CDATA #REQUIRED>
+
+<!ELEMENT sc_embedded_manifest (#PCDATA)>
+<!ATTLIST sc_embedded_manifest name CDATA #REQUIRED>
+
+<!ELEMENT ai_instance (target*, software+, add_drivers?, (configuration*|sc_manifest_file|sc_embedded_manifest), source*)>
+<!ATTLIST ai_instance name CDATA #REQUIRED>
+<!ATTLIST ai_instance http_proxy CDATA #IMPLIED>
+<!ATTLIST ai_instance auto_reboot (true|false) "false">
+
+<!ELEMENT add_drivers (software*, search_all?)>
+
+<!ELEMENT search_all (source?)>
+<!ATTLIST search_all addall (true|false) "false">
--- a/usr/src/cmd/auto-install/ai_manifest.defval.xml	Tue Aug 17 18:22:44 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-<!--
-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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
-Use is subject to license terms.
--->
-
-<!--
-===============================================================================
-RelaxNG default manifest for Automatic Installer input manifest specification.
-===============================================================================
--->
-
-
-<defaults_and_validation_manifest name="version 0.1">
-	<helpers>
-	</helpers>
-	<default nodepath="ai_pkg_repo_default_publisher/main/url"
-	    from="value" type="attribute" missing_parent="skip">
-	        http://pkg.opensolaris.org/release
-	</default>
-	<default nodepath="ai_pkg_repo_default_publisher/main/publisher"
-	    from="value" type="attribute" missing_parent="skip">
-	        opensolaris.org
-	</default>
-	<default nodepath="ai_pkg_repo_default_authority/main/url"
-	    from="value" type="attribute" missing_parent="skip">
-	        http://pkg.opensolaris.org/release
-	</default>
-	<default nodepath="ai_pkg_repo_default_authority/main/authname"
-	    from="value" type="attribute" missing_parent="skip">
-	        opensolaris.org
-	</default>
-	<default nodepath="ai_packages/package_name"
-	    from="value" type="element" missing_parent="skip">
-	        SUNWcsd
-		SUNWcs
-		babel_install
-		entire
-	</default>
-	<default nodepath="ai_device_partitioning/partition_number"
-	    from="value" type="element" missing_parent="skip">
-		0
-	</default>
-	<default nodepath="ai_device_partitioning/partition_start_sector"
-	    from="value" type="element" missing_parent="skip">
-		-1
-	    <!--
-	    	default -1 is interpreted as no start sector specified
-	    	AI engine will find best location
-	    -->
-	</default>
-	<default nodepath="ai_device_partitioning/partition_type"
-	    from="value" type="element" missing_parent="skip">
-		191	<!-- 191=Solaris2 -->
-	</default>
-	<default nodepath="ai_device_partitioning/partition_size"
-	    from="value" type="element" missing_parent="skip">
-		0
-	</default>
-	<default nodepath="ai_device_partitioning/partition_size"
-	    from="value" type="element" missing_parent="skip">
-		0
-	</default>
-	<default nodepath="ai_device_partitioning/partition_is_logical"
-	    from="value" type="element" missing_parent="skip">
-		false
-	</default>
-	<!-- use this to override units for all partition size specifications -->
-	<default nodepath="ai_device_partitioning/partition_size_units"
-	    from="value" type="element" missing_parent="skip">
-		mb
-	</default>
-	<!-- use this to override units for all slice size specifications -->
-	<default nodepath="ai_device_vtoc_slices/slice_size_units"
-	    from="value" type="element" missing_parent="skip">
-		mb
-	</default>
-	<default nodepath="ai_device_vtoc_slices/slice_size"
-	    from="value" type="element" missing_parent="skip">
-		0
-	</default>
-	<default nodepath="ai_device_vtoc_slices/slice_on_existing"
-	    from="value" type="element" missing_parent="skip">
-		error
-	</default>
-</defaults_and_validation_manifest>
--- a/usr/src/cmd/auto-install/ai_manifest.rng	Tue Aug 17 18:22:44 2010 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,734 +0,0 @@
-<?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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
--->
-
-<!--
-===============================================================================
-RelaxNG schema for Automatic Installer input manifest specification.
-
-Contains schema rules and content specification for AI manifest.
-
-===============================================================================
--->
-
-
-<grammar
-    xmlns="http://relaxng.org/ns/structure/1.0"
-    datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-
-        <start>
-                <element name="ai_manifest">
-			<attribute name="name"/>
-                        <!-- General automatic Installation parameters -->
-                        <ref name="auto_installation_manifest"/>
-                </element>
-        </start>
-
-        <!--
-        =======================================================================
-        Parameters used for Automatic installation
-        =======================================================================
-        -->
-        <define name="auto_installation_manifest">
-                <interleave>      <!-- Any element order is OK. -->
-			<optional>
-				<element name="ai_add_drivers">
-					<ref name="ai_add_drivers_contents"/>
-				</element>
-			</optional>
-
-			<optional>
-                		<element name="ai_target_device">
-					<ref name="ai_target_device_contents"/>
-				</element>
-			</optional>
-		
-			<zeroOrMore>
-                		<element name="ai_device_partitioning">
-					<ref name="ai_device_partitioning_contents"/>
-				</element>
-			</zeroOrMore>
-		
-			<zeroOrMore>
-                		<element name="ai_device_vtoc_slices">
-					<ref name="ai_device_vtoc_slices_contents"/>
-				</element>
-			</zeroOrMore>
-			
-			<optional>
-                		<element name="ai_device_zfs_root_pool">
-					<ref name="ai_zfs_root_pool_contents"/>
-				</element>
-			</optional>
-		
-			<optional>
-				<element name="ai_swap_device">
-					<ref name="ai_swap_device_contents"/>
-				</element>
-			</optional>
-
-			<optional>
-				<element name="ai_dump_device">
-					<ref name="ai_dump_device_contents"/>
-				</element>
-			</optional>
-
-			<optional>
-				<choice>
-					<element name="ai_pkg_repo_default_publisher">
-						<ref name="ai_pkg_repo_contents"/>
-					</element>
-					<element name="ai_pkg_repo_default_authority">
-						<ref name="ai_pkg_repo_contents"/>
-					</element>
-				</choice>
-			</optional>
-
-			<zeroOrMore>
-				<choice>
-					<element name="ai_pkg_repo_addl_publisher">
-						<ref name="ai_pkg_repo_contents"/>
-					</element>
-					<element name="ai_pkg_repo_addl_authority">
-						<ref name="ai_pkg_repo_contents"/>
-					</element>
-				</choice>
-			</zeroOrMore>
-
-			<!--
-			========================================================
-			Define AI packages
-			========================================================
-			-->
-			<choice>
-				<element name="ai_packages">
-					<zeroOrMore>
-						<element name="package_name">
-							<text/>
-						</element>
-					</zeroOrMore>
-				</element>
-
-				<element name="ai_install_packages">
-					<oneOrMore>
-						<element name="pkg">
-							<attribute name="name">
-								<text/>
-							</attribute>
-						</element>
-					</oneOrMore>
-				</element> 
-			</choice>
-
-			<optional>
-				<element name="ai_uninstall_packages">
-					<oneOrMore>
-						<element name="pkg">
-							<attribute name="name">
-								<text/>
-							</attribute>
-						</element>
-					</oneOrMore>
-				</element>
-			</optional>
-
-			<optional>
-				<element name="ai_http_proxy">
-					<ref name="ai_http_proxy_contents"/>
-				</element>
-			</optional>
-
-			<optional>
-				<element name="ai_auto_reboot">
-					<data type="boolean"/>
-				</element>
-			</optional>		
-		</interleave>
-	</define>
-
-        <!--
-        =======================================================================
-        Selections for AI Driver Update (add_drivers) specification
-        =======================================================================
-        -->
-	<define name="ai_add_drivers_contents">
-		<choice>
-			<oneOrMore>
-				<ref name="ai_bundle_contents"/>
-			</oneOrMore>
-			<group>
-				<zeroOrMore>
-					<ref name="ai_bundle_contents"/>
-				</zeroOrMore>
-				<ref name="ai_searchall_contents"/>
-			</group>
-		</choice>
-	</define>
-
-	<define name="ai_bundle_contents">
-		<element name="bundle">
-			<choice>
-				<attribute name="type">
-					<choice>
-						<value>P5I</value>
-						<value>DU</value>
-					</choice>
-				</attribute>
-				<group>
-					<attribute name="type">
-						<value>SVR4</value>
-					</attribute>
-					<attribute name="name"/>
-				</group>
-			</choice>
-			<attribute name="location"/>
-			<optional>
-				<attribute name="noinstall">
-					<!--
-					lower case true/false
-					-->
-					<data type="boolean"/>
-				</attribute>
-			</optional>
-		</element>
-	</define>
-
-	<define name="ai_searchall_contents">
-		<element name="searchall">
-			<optional>
-				<attribute name="publisher"/>
-				<attribute name="location"/>
-			</optional>
-			<optional>
-				<attribute name="addall">
-					<!--
-					lower case true/false
-					-->
-					<data type="boolean"/>
-				</attribute>
-			</optional>
-		</element>
-	</define>
-
-        <!--
-        =======================================================================
-        Selections for AI target Device specification
-
-	Disk criteria are divided into two mutually exclusive groups:
-
-	G1 - deterministic disk criteria
-	................................
-	  * iscsi_target_parameters
-	  * target_device_name (c#t#d# name or 'boot_disk')
-	  * target_device_select_volume_name
-	  * target_device_select_id
-	  * target_device_select_device_path
-
-	G2 - rest of disk criteria
-	..........................
-	  * target_device_type
-	  * target_device_vendor
-	  * target_device_size
-
-	Schema enforces following policy:
-
-	* criteria in group G1 are mutually exclusive - only
-	  one can be specified at a time
-
-	* groups G1 and G2 are mutually exclusive - i.e.
-	  if criteria from G1 is specified, no criteria
-	  from G2 are allowed and vice versa
-
-	* multiple criteria from G2 can be specified
-        =======================================================================
-        -->
-        <define name="ai_target_device_contents">
-		<interleave>
-			<choice>
-				<ref name="iscsi_target_parameters"/>
-	                	<!--
-				    c#t#d# device name like c0t0d0 or 
-				    MPXIO name like c0t2000002037CD9F72d0
-				    or 'boot_disk' keyword to select boot disk
-				-->
-				<element name="target_device_name">
-					<text/>
-				</element>
-
-	                	<!-- volume name set for instance by means
-				    of format(1M) command -->
-				<element name="target_device_select_volume_name">
-					<text/>
-				</element>
-
-	                	<!-- device id - e.g. can be obtained by means of
-				    iostat(1M) -iEn -->
-                       		<element name="target_device_select_id">
-					<text/>
-				</element>
-
-	                	<!-- device path under /devices directory, e.g.
-				    /pci@1e,600000/pci@0/pci@9/pci@0/scsi@1/sd@0,0 -->
-                       		<element name="target_device_select_device_path">
-					<text/>
-				</element>
-				<group>
-					<interleave>
-						<optional>
-							<element name="target_device_type">
-								<text/>
-							</element>
-						</optional>
-						<optional>
-							<element name="target_device_vendor">
-								<text/>
-							</element>
-						</optional>
-						<optional>
-							<element name="target_device_size">
-								<text/>
-							</element>
-						</optional>
-					</interleave>
-				</group>
-			</choice>
-			<optional>
-				<element name="target_device_install_slice_number">
-					<choice>
-						<value>0</value>
-						<value>1</value>
-						<value>2</value>
-						<value>3</value>
-						<value>4</value>
-						<value>5</value>
-						<value>6</value>
-						<value>7</value>
-					</choice>
-				</element>
-			</optional>
-			<optional>
-				<element name="target_device_use_solaris_partition">
-					<data type="boolean"/>
-				</element>
-			</optional>
-			<optional>
-				<element name="target_device_overwrite_root_zfs_pool">
-					<data type="boolean"/>
-				</element>
-			</optional>
-		</interleave>
-	</define>
-
-        <!--
-        =======================================================================
-	iSCSI boot parameters - target IP and name are mandatory
-        =======================================================================
-        -->
-        <define name="iscsi_target_parameters">
-		<interleave>
-			<optional>
-				<element name="target_device_iscsi_parameter_source">
-					<text/>
-				</element>
-			</optional>
-			<optional>
-				<interleave>
-					<element name="target_device_iscsi_target_name">
-						<text/>
-					</element>
-					<element name="target_device_iscsi_target_ip">
-						<text/>
-					</element>
-					<optional>
-						<element name="target_device_iscsi_target_lun">
-							<text/>
-						</element>
-					</optional>
-					<optional>
-						<element name="target_device_iscsi_target_port">
-							<data type="unsignedLong"/>
-						</element>
-					</optional>
-				</interleave>
-			</optional>
-		</interleave>
-	</define>
-
-        <!--
-        =======================================================================
-        Selections for AI target device partitions specification
-        =======================================================================
-        -->
-        <define name="ai_device_partitioning_contents">
-		<choice>
-			<group>
-				<interleave>
-					<element name="partition_action">
-						<value>delete</value>
-					</element>
-					<choice> <!-- delete partition -->
-						<group> <!-- by partition number -->
-							<interleave>
-								<ref name="partition_number_contents"/>
-								<optional>
-									<ref name="partition_size_contents"/>
-								</optional>
-								<optional>
-									<ref name="partition_start_sector_contents"/>
-								</optional>
-								<optional>
-									<ref name="partition_size_units_contents"/>
-								</optional>
-								<optional>
-									<ref name="partition_type_contents"/>
-								</optional>
-								<optional>
-									<ref name="partition_is_logical_contents"/>
-								</optional>
-							</interleave>
-						</group>
-						<group> <!-- by starting sector and optionally size/units -->
-							<interleave>
-								<ref name="partition_start_sector_contents"/>
-								<optional>
-									<ref name="partition_size_contents"/>
-								</optional>
-								<optional>
-									<ref name="partition_size_units_contents"/>
-								</optional>
-								<optional>
-									<ref name="partition_number_contents"/>
-								</optional>
-								<optional>
-									<ref name="partition_type_contents"/>
-								</optional>
-								<optional>
-									<ref name="partition_is_logical_contents"/>
-								</optional>
-							</interleave>
-						</group>
-					</choice>
-				</interleave>
-			</group>
-			<group> <!-- create partition by size with optional units, ID, starting sector, primary/logical -->
-				<interleave>
-					<element name="partition_action">
-						<value>create</value>
-					</element>
-					<ref name="partition_size_contents"/>
-					<optional>
-						<ref name="partition_size_units_contents"/>
-					</optional>
-					<optional>
-						<ref name="partition_type_contents"/>
-					</optional>
-					<optional>
-						<ref name="partition_start_sector_contents"/>
-					</optional>
-					<optional>
-						<ref name="partition_is_logical_contents"/>
-					</optional>
-					<optional>
-						<ref name="partition_number_contents"/>
-					</optional>
-				</interleave>
-			</group>
-		</choice>
-	</define>
-        <define name="partition_number_contents">
-		<element name="partition_number">
-			<data type="unsignedByte"/>
-		</element>
-	</define>
-        <define name="partition_size_contents">
-		<element name="partition_size">
-			<!-- integer or "max_size" to use largest free region -->
-			<choice>
-				<data type="unsignedLong"/>
-				<value>max_size</value>
-				<value>MAX_SIZE</value>
-			</choice>
-		</element>
-	</define>
-        <define name="partition_start_sector_contents">
-                 <element name="partition_start_sector">
-			<data type="long"/>
-		</element>
-	</define>
-        <define name="partition_size_units_contents">
-		<element name="partition_size_units">
-			<ref name="disk_space_size_units"/>
-		</element>
-	</define>
-        <define name="partition_type_contents">
-		<element name="partition_type">
-			<text/>
-		</element>
-	</define>
-        <define name="partition_is_logical_contents">
-		<element name="partition_is_logical">
-			<data type="boolean"/>
-		</element>
-	</define>
-        <!--
-        =======================================================================
-        Selections for AI target device vtoc slices specification
-        =======================================================================
-        -->
-        <define name="ai_device_vtoc_slices_contents">
-		<choice>
-			<!-- create slice, providing number, size and optionally units -->
-			<group>
-				<interleave>
-					<element name="slice_action">
-						<value>create</value>
-					</element>
-					<ref name="slice_number_contents"/>
-					<ref name="slice_size_contents"/>
-					<optional>
-						<ref name="slice_size_units_contents"/>
-					</optional>
-					<optional>
-						<!-- define behavior on create action if specified slice exists -->
-						<ref name="slice_on_existing_contents"/>
-					</optional>
-				</interleave>
-			</group>
-			<!-- delete slice - by slice number only -->
-			<group>
-				<interleave>
-					<element name="slice_action">
-						<value>delete</value>
-					</element>
-					<ref name="slice_number_contents"/>
-					<optional>
-						<ref name="slice_size_contents"/>
-					</optional>
-					<optional>
-						<ref name="slice_size_units_contents"/>
-					</optional>
-					<optional>
-						<ref name="slice_on_existing_contents"/>
-					</optional>
-				</interleave>
-			</group>
-			<!-- preserve slice - by slice number only -->
-			<group>
-				<interleave>
-					<element name="slice_action">
-						<value>preserve</value>
-					</element>
-					<ref name="slice_number_contents"/>
-					<optional>
-						<ref name="slice_size_contents"/>
-					</optional>
-					<optional>
-						<ref name="slice_size_units_contents"/>
-					</optional>
-					<optional>
-						<ref name="slice_on_existing_contents"/>
-					</optional>
-				</interleave>
-			</group>
-		</choice>
-	</define>
-	<define name="slice_number_contents">
-		<element name="slice_number">
-			<data type="unsignedByte"/>
-		</element>
-	</define>
-	<define name="slice_size_contents">
-              	<element name="slice_size">
-			<!-- integer or "max_size" to use largest free region -->
-			<choice>
-				<data type="unsignedLong"/>
-				<value>max_size</value>
-				<value>MAX_SIZE</value>
-			</choice>
-		</element>
-	</define>
-        <define name="slice_size_units_contents">
-		<element name="slice_size_units">
-			<ref name="disk_space_size_units"/>
-		</element>
-	</define>
-        <define name="slice_on_existing_contents">
-               	<element name="slice_on_existing">
-			<choice>
-				<value>error</value>
-				<value>ERROR</value>
-				<value>Error</value>
-				<value>overwrite</value>
-				<value>OVERWRITE</value>
-				<value>Overwrite</value>
-			</choice>
-		</element>
-	</define>
-
-        <!--
-        =======================================================================
-        Selections for AI ZFS Root Pool specification
-        =======================================================================
-        -->
-        <define name="ai_zfs_root_pool_contents">
-		<interleave>
-			<optional>
-                       		<element name="enable_mirrored_root">
-					<data type="boolean"/>
-				</element>
-			</optional>
-			<optional>
-                       		<element name="zfs_options">
-					<text/>
-				</element>
-			</optional>
-		</interleave>
-	</define>
-
-        <!--
-        =======================================================================
-        Selections for AI swap specification
-	The target device will be used as swap device
-        =======================================================================
-        -->
-        <define name="ai_swap_device_contents">
-               <element name="ai_swap_size">
-			<data type="unsignedLong"/>
-		</element>
-	</define>
-
-        <!--
-        =======================================================================
-        Selections for AI dump specification
-	The target device will be used as dump device
-        =======================================================================
-        -->
-        <define name="ai_dump_device_contents">
-               <element name="ai_dump_size">
-			<data type="unsignedLong"/>
-		</element>
-	</define>
-
-        <!--
-        =======================================================================
-        Define a publisher and its mirror backups.
-        =======================================================================
-        -->
-        <define name="ai_pkg_repo_contents">
-                <element name="main">
-                        <ref name="ai_repo_name"/>
-                </element>
-                <zeroOrMore>
-                        <element name="mirror">
-                                <ref name="ai_mirror_name"/>
-                        </element>
-                </zeroOrMore>
-        </define>
-
-	<define name="ai_repo_name">
-		<choice>
-			<attribute name="publisher">
-				<text/>
-			</attribute>
-			<attribute name="authname">
-				<text/>
-			</attribute>
-		</choice>
-		<attribute name="url">
-			<text/>
-		</attribute>
-	</define>
-
-	<define name="ai_mirror_name">
-		<attribute name="url">
-			<text/>
-		</attribute>
-	</define>	
-
-        <!--
-        =======================================================================
-        Define AI proxy
-        =======================================================================
-        -->
-	<define name="ai_http_proxy_contents">
-		<attribute name="url">
-			<text/>
-		</attribute>
-	</define>	
-        <!--
-        =======================================================================
-        General definitions
-        =======================================================================
-        -->
-        <!--
-        =======================================================================
-        Units measuring sizable regions of space on disk drives
-        =======================================================================
-        -->
-        <define name="disk_space_size_units">
-		<choice>
-			<value>sectors</value>	<!-- disk space sectors (512 bytes) -->
-			<value>secs</value>
-			<value>sec</value>
-			<value>s</value>
-			<value>megabytes</value>
-			<value>megabyte</value>
-			<value>mib</value>
-			<value>mb</value>
-			<value>m</value>
-			<value>gigabytes</value>
-			<value>gigabyte</value>
-			<value>gib</value>
-			<value>gb</value>
-			<value>g</value>
-			<value>terabytes</value>
-			<value>terabyte</value>
-			<value>tib</value>
-			<value>tb</value>
-			<value>t</value>
-			<value>SECTORS</value>	<!-- disk space sectors (512 bytes) -->
-			<value>SECS</value>
-			<value>SEC</value>
-			<value>S</value>
-			<value>MEGABYTES</value>
-			<value>MEGABYTE</value>
-			<value>MIB</value>
-			<value>MiB</value>
-			<value>MB</value>
-			<value>M</value>
-			<value>GIGABYTES</value>
-			<value>GIGABYTE</value>
-			<value>GIB</value>
-			<value>GiB</value>
-			<value>GB</value>
-			<value>G</value>
-			<value>TERABYTES</value>
-			<value>TERABYTE</value>
-			<value>TIB</value>
-			<value>TiB</value>
-			<value>TB</value>
-			<value>T</value>
-		</choice>
-	</define>
-
-</grammar>
--- a/usr/src/cmd/auto-install/ai_manifest.xml	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/ai_manifest.xml	Fri Aug 20 11:31:18 2010 -0600
@@ -1,3 +1,4 @@
+<?xml version="1.0"?>
 <!--
 CDDL HEADER START
 
@@ -20,196 +21,221 @@
 
 Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 -->
-
 <!--
 ===============================================================================
-RelaxNG sample manifest for Automatic Installer input manifest specification.
+DTD sample manifest for Automatic Installer input manifest specification.
 ===============================================================================
 -->
-<ai_manifest name="sample_ai_manifest">
-	<!--
-	=======================================================================
-	<ai_target_device> - selections for AI target Device specification
-
-	Disk criteria are divided into two mutually exclusive groups:
+<!DOCTYPE auto_install SYSTEM "file:///usr/share/auto_install/ai.dtd">
+<auto_install>
+  <!--
+	"auto_reboot" set to "true" may be an issue for x86 machines.
+	The boot order is not guaranteed and may cause unexpected
+	behavior. If auto_reboot is not desired, removing this
+	attribute, e.g. <ai_instance name="sample_ai_manfiest">
+	will work. "auto_reboot" is set to false by default. 
+    -->
+  <ai_instance name="sample_ai_manifest" auto_reboot="true">
+    <!--
+      =======================================================================
+      <target/target_device> - selections for AI target Device specification
 
-	G1 - deterministic disk criteria
-	................................
-	  * iscsi_target_parameters
-	  * target_device_name (c#t#d# name or 'boot_disk')
-	  * target_device_select_volume_name
-	  * target_device_select_id
-	  * target_device_select_device_path
-
-	G2 - rest of disk criteria
-	..........................
-	  * target_device_type
-	  * target_device_vendor
-	  * target_device_size
+      Disk criteria are divided into three mutually exclusive groups:
 
-	Schema ai_manifest.rng enforces following policy:
-
-	* criteria in group G1 are mutually exclusive - only
-	  one can be specified at a time
-
-	* groups G1 and G2 are mutually exclusive - i.e.
-	  if criteria from G1 is specified, no criteria
-	  from G2 are allowed and vice versa
+      G1 - deterministic disk criteria
+      ................................
+        * target_device/disk/iscsi parameters
+        * target_device/disk/disk_name, with name_type attribute:
+          one of ctd, volid, devpath or devid
 
-	* multiple criteria from G2 can be specified
-        =======================================================================
-        -->
-	<ai_target_device>
-		<!-- G1 -->
-               	<!--
-		    c#t#d# device name like c0t0d0 or 
-		    MPXIO name like c0t2000002037CD9F72d0
-		    or 'boot_disk' keyword to select boot disk
-		-->
-		<target_device_name>
-			boot_disk
-		</target_device_name>
+      G2 - non-deterministic disk criteria
+      ..........................
+        * target_device/disk/disk_prop: Any of dev_type, dev_vendor or
+          dev_size
+
+      G3 - keyword disk criteria
+      ...........................
+        * target_device/disk/disk_keyword: "boot_disk"
+
+      Schema ai.dtd enforces following policy:
+
+      * criteria in group G1 are mutually exclusive - only
+        one can be specified at a time
 
-               	<!-- volume name set for instance by means
-		    of format(1M) command
-		-->
-		<!--
-		    <target_device_select_volume_name>
-			ai-disk
-		    </target_device_select_volume_name>
-		-->
-
-               	<!-- device id - e.g. can be obtained by means of
-		    iostat(1M) -iEn
-		-->
-		<!--
-		    <target_device_select_id>
-			id1,cmdk@AST31000340NS=____________9QJ2LNYY
-		    </target_device_select_id>
-		-->
+      * groups G1, G2 and G3 are mutually exclusive - i.e.
+        if criteria from G1 is specified, no criteria
+        from G2 or G3 are allowed and vice versa
 
-               	<!-- device path under /devices directory, e.g.
-		    /pci@1e,600000/pci@0/pci@9/pci@0/scsi@1/sd@0,0
-		-->
-		<!--
-		    <target_device_select_device_path>
-			/pci@0/pci@9/pci@0/scsi@1/sd@0,0
-		    </target_device_select_device_path>
-		-->
-
-		<!-- G2 -->
-		<!--
-		    <target_device_type>SCSI</target_device_type>
-		    <target_device_vendor>hitachi</target_device_vendor>
-		    <target_device_size>20480</target_device_size>
-		-->
-
-		<!--
-		    Uncomment this to force AI to find an existing Solaris
-		    partition instead of creating a new one.
-		-->
-		<!--
-		    <target_device_use_solaris_partition>
-			true
-		    </target_device_use_solaris_partition>
-		-->
+      * multiple criteria from G2 can be specified
+      =======================================================================
+    -->
+    <target>
+      <target_device>
+        <disk>
+          <!-- G1 -->
+          <!--
+            c#t#d# device name like c0t0d0 or 
+            MPXIO name like c0t2000002037CD9F72d0
+          -->
+          <disk_name name="c1t0d0" name_type="ctd"/>
+          <!-- volume name set for instance by means
+            of format(1M) command
+          -->
+          <!--
+          <disk_name name="ai-disk" name_type="volid"/>
+          -->
+          <!-- device id - e.g. can be obtained by means of
+            iostat(1M) -iEn
+          -->
+          <!--
+          <disk_name name="id1,cmdk@AST31000340NS=____________9QJ2LNYY" name_type="devid"/>
+          -->
+          <!-- device path under /devices directory, e.g.
+            /pci@1e,600000/pci@0/pci@9/pci@0/scsi@1/sd@0,0
+          -->
+          <!--
+          <disk_name name="/pci@0/pci@9/pci@0/scsi@1/sd@0,0" name_type="devpath"/>
+          -->
+          <!--
+            ISCSI target device
 
-		<target_device_install_slice_number>
-		    0
-		</target_device_install_slice_number>
-	</ai_target_device>
-	<ai_device_partitioning>
-		<partition_action>create</partition_action>
-		<partition_number>1</partition_number>
-		<partition_start_sector>200</partition_start_sector>
-		<partition_size>20480</partition_size>
-		<partition_type>99</partition_type>
-	</ai_device_partitioning>
-	<ai_device_partitioning>
-		<partition_action>create</partition_action>
-		<partition_number>4</partition_number>
-		<partition_start_sector>200</partition_start_sector>
-		<partition_size>20480</partition_size>
-		<partition_size_units>mb</partition_size_units>
-		<partition_type>99</partition_type>
-	</ai_device_partitioning>
-	<ai_device_vtoc_slices>
-		<slice_action>create</slice_action>
-		<slice_number>4</slice_number>
-		<slice_size>20480</slice_size>
-		<slice_size_units>mb</slice_size_units>
-	</ai_device_vtoc_slices>
-	<ai_device_vtoc_slices>
-		<slice_action>delete</slice_action>
-		<slice_number>4</slice_number>
-		<slice_size>20480</slice_size>
-	</ai_device_vtoc_slices>
-	<ai_pkg_repo_default_publisher>
-		<main url="http://pkg.opensolaris.org/release"
-		    publisher="opensolaris.org"/>
-		<mirror url=""/>
-	</ai_pkg_repo_default_publisher>
-
-	<!--
-	    By default the latest build available, in the
-	    specified IPS repository, is installed.
-	    If another build is required, the build number has
-	    to be appended to the 'entire' package in following
-	    form:
+          <iscsi name="c0d2E0001010F68">
+            <ip>192.168.1.34</ip>
+          </iscsi> 
+          -->
+          <!-- G2 -->
+          <!--
+          <disk_prop dev_vendor="hitachi" dev_size="20480mb"/>
+          -->
+          <!-- G3 -->
+          <!--
+          <disk_keyword key="boot_disk"/>
+          -->
+          <!--
+            Uncomment this to force AI to find an existing Solaris
+            partition instead of creating a new one.
+          -->
+          <!--
+          <partition action="use_existing"/>
+          -->
+          <partition name="1" part_type="99">
+            <size start_sector="200" val="20480mb"/>
+          </partition>
+          <partition name="4" part_type="99">
+            <size start_sector="2000" val="20480mb"/>
+          </partition>
+          <slice name="0" is_root="true">
+            <size val="20480mb"/>
+          </slice>
+          <slice name="4">
+            <size val="20480mb"/>
+          </slice>
+        </disk>
+      </target_device>
+    </target>
+    <software name="ips">
+      <source>
+        <publisher name="opensolaris.org">
+          <origin name="http://pkg.opensolaris.org/release"/>
+        </publisher>
+      </source>
+      <!--
+        By default the latest build available, in the
+        specified IPS repository, is installed.
+        if another build is required, the build number has
+        to be appended to the 'entire' package in following
+        form:
+      <name="[email protected]#"/>
+      -->
+      <software_data type="IPS">
+        <name>pkg:/entire</name>
+        <name>pkg:/babel_install</name>
+      </software_data>
+      <!--
+          babel_install and slim_install are group packages used to
+          define the default installation.  They are removed here so
+          that they do not inhibit removal of other packages on the installed
+          system
+      -->
+      <software_data action="uninstall" type="IPS">
+        <name>pkg:/babel_install</name>
+        <name>pkg:/slim_install</name>
+      </software_data>
+    </software>
+    <add_drivers>
+      <!--
+	    Driver Updates: This section is for adding driver packages to the
+            boot environment before the installation takes place.  The
+            installer can then access all devices on the system.  The
+            packages installed in the boot environment will also be installed
+            on the target.
 
-	    <pkg_name="[email protected]#/>
-	-->
-	<ai_install_packages>
-		<pkg name="entire"/>
-		<pkg name="babel_install"/>
-	</ai_install_packages>
-	<!--
-	    babel_install and slim_install are group packages used to
-	    define the default installation.  They are removed here so
-	    that they do not inhibit removal of other packages on the installed
-	    system
-	-->
-	<ai_uninstall_packages>
-		    <pkg name="babel_install"/>
-		    <pkg name="slim_install"/>
-	</ai_uninstall_packages>
-	<ai_auto_reboot>
-		false
-	</ai_auto_reboot>
-        <ai_add_drivers>
-		<!--
-		bundles are specifications of packages needed in order to
-		perform the install.  types are as follows:
+            A <search_all> entry performs a search for devices which are
+            missing their drivers.  A repository publisher and location
+            may be specified, and that repository and its database will
+            be used.  If no publisher and location is specified, the
+            configured repositories will be used.
+            (See pkg publisher command.)  If <addall> is specified as
+            "true", then drivers the database says are third-party drivers
+            will be added like all others; otherwise third-party drivers
+            will not be added.
+
+                <search_all addall="true">
+                    <source>
+                        <publisher name="opensolaris.org">
+                            <origin name="http://pkg.opensolaris.org/release"/>
+                        </publisher>
+                    </source>
+                </search_all>
+
+            <software> entries are user-provided specifications of packages
+            needed in order to perform the install.  types are P5I, SVR4, DU.
+            A <software_data> action of "noinstall" inhibits adding to target.
+
+            P5I: A pkg(5) P5I file, full path is in the source/publisher/origin.
+            Path may be to a local file or an http or ftp specification.
+                <software>
+                    <source>
+                        <publisher>
+                            <origin
+				name=
+		"http://pkg.opensolaris.org/release/p5i/0/driver/firewire.p5i"/>
+                        </publisher>
+                    </source>
+		    <software_data type="P5I"/>
+                </software>
 
-		P5I: A pkg(5) P5I file, full path is in the "location" attr.
-		Path may be to a local file or an http or ftp specification.
-		<bundle type="P5I"
-		    location=
-		    "http://pkg.opensolaris.org/release/p5i/0/SUNW1394.p5i"/>
+            SVR4: An SVR4 package spec. The source/publisher/origin corresponds
+            to the directory containing the packages.  The 
+	    software/software_data/name refers tp the package's top level
+	    directory or the package's datastream file.
 
-		SVR4: An SVR4 package spec.  location corresponds to what
-		pkgadd -d would refer to.  Name refers to the package name.
-		<bundle type="SVR4" location="/export/package_dir"
-		    name="SUNW1394h"/>
-
-		DU: An ITU (Install Time Update) or Driver Update image.
-		location refers to the path just above the image's DU directory.
-		<bundle type="DU" location="/export/duimages/mydriver"
-		    name="mydriverDU"/>
+                <software>
+                    <source>
+                        <publisher>
+                            <origin name="/export/package_dir"/>
+                        </publisher>
+                    </source>
+                    <software_data type="SVR4">
+                        <name>my_disk_driver.d</name>
+                    </software_data>
+                </software>
 
-		A <searchall> entry performs a search for devices which are
-		missing their drivers.  It attempts to locate the correct
-		drivers via a database.  A repository publisher and location
-		may be specified, and that repository and its database will
-		be used.  If no publisher and location is specified, the
-		system's configured repositories will be used.
-		(See pkg publisher command.)  If <addall> is specified as
-		"true", then drivers the database says are third-party drivers
-		will be added like all others;  otherwise third-party drivers
-		will not be added.
-		    <searchall publisher="opensolaris.org"
-			location="http://ipkg.sfbay/dev" addall="true"/>
-		-->
-		<searchall/>
-	</ai_add_drivers>
-</ai_manifest>
+            DU: An ITU (Install Time Update) or Driver Update image.
+            The source/publisher/origin refers to the path just above the 
+	    image's DU directory (if expanded) or the name of the .iso image.  
+	    All packages in the image will be added.
+
+                <software>
+                    <source>
+                        <publisher>
+                            <origin name="/export/duimages/mydriver.iso"/>
+                        </publisher>
+                    </source>
+                    <software_data type="DU"/>
+                </software>	
+      -->
+      <search_all/>
+    </add_drivers>
+  </ai_instance>
+</auto_install>
--- a/usr/src/cmd/auto-install/ai_parse_manifest.py	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/ai_parse_manifest.py	Fri Aug 20 11:31:18 2010 -0600
@@ -42,7 +42,8 @@
     """
 
     try:
-        manifest_obj = ManifestServ(manifest_file, full_init=False)
+        manifest_obj = ManifestServ(manifest_file, full_init=False,
+                                    dtd_schema=True)
     except StandardError, err:
         print "Error creating in-memory copy of Manifest data."
         print str(err)
@@ -55,7 +56,7 @@
 def ai_setup_manifestserv(manifest_obj):
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     """
-        Sets defaults and validates a manifest server object
+        Validates a manifest server object
 
         Args:
 	  manifest_server_obj: ManifestServ object containing data to validate.
@@ -66,9 +67,7 @@
     """
 
     try:
-        manifest_obj.set_defaults()
-        manifest_obj.schema_validate()
-        manifest_obj.semantic_validate()
+        manifest_obj.schema_validate(dtd_schema=True)
         return 0
 
     except StandardError, err:
--- a/usr/src/cmd/auto-install/auto_ddu_lib.c	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/auto_ddu_lib.c	Fri Aug 20 11:31:18 2010 -0600
@@ -48,21 +48,22 @@
 #define	ICT_UPDATE_ARCHIVE	"update_boot_archive"
 
 /* AI Manifest (AIM) related path definitions. */
-#define	AIM_PREFACE		"ai_manifest/ai_add_drivers/"
-#define	BUNDLE_NODEPATH		"bundle"
-#define	LOCN_NODEPATH		"bundle/location"
-#define	TYPE_NODEPATH		"bundle[location=\"%s\"]/type"
-#define	NAME_NODEPATH		"bundle[location=\"%s\":type=\"%s\"]/name"
-#define	NOINSTALL_NONAME_NODEPATH \
-				"bundle[location=\"%s\":type=\"%s\"]/noinstall"
-#define	NOINSTALL_YESNAME_NODEPATH \
-				"bundle[location=\"%s\":type=" \
-				    "\"%s\":name=\"%s\"]/noinstall"
+#define	AIM_PREFACE		    "auto_install/ai_instance/add_drivers/"
+#define	PKGSPEC_NODEPATH	"software"
+#define	ORIGIN_NODEPATH		"software/source/publisher/origin/name"
+#define	TYPE_NODEPATH \
+	"software[source/publisher/origin/name=\"%s\"]/software_data/type"
+#define	NAME_NODEPATH \
+	"software[source/publisher/origin/name=\"%s\":software_data/type=\"%s\"]/software_data/name"
+#define	ACTION_NONAME_NODEPATH \
+	"software[source/publisher/origin/name=\"%s\":software_data/type=\"%s\"]/software_data/action"
+#define	ACTION_YESNAME_NODEPATH \
+	"software[source/publisher/origin/name=\"%s\":software_data/type=\"%s\":software_data/name=\"%s\"]/software_data/action"
 
-#define	SEARCH_NODEPATH		"searchall"
-#define	SEARCH_LOCN_NODEPATH	"searchall/location"
-#define	SEARCH_PUB_NODEPATH	"searchall/publisher"
-#define	SEARCH_ADDALL_NODEPATH	"searchall/addall"
+#define	SEARCH_NODEPATH		"search_all"
+#define	SEARCH_ORIGIN_NODEPATH	"search_all/source/publisher/origin/name"
+#define	SEARCH_PUBNAME_NODEPATH	"search_all/source/publisher/name"
+#define	SEARCH_ADDALL_NODEPATH	"search_all/addall"
 
 #define	MAX_NODEPATH_SIZE	256
 
@@ -89,20 +90,20 @@
 static int ai_call_ddu_install_package(py_state_t *py_state_p,
     PyObject *ddu_package_obj, char *install_root, boolean_t third_party_ok);
 static PyObject *ai_new_ddu_package_object(py_state_t *py_state_p,
-    char *type, char *name, char *location);
+    char *type, char *name, char *origin);
 static int ai_get_ddu_package_object_values(PyObject *pDDUPackageObject,
-    char **type, char **location, char **name, char **descr, char **inf_link,
+    char **type, char **origin, char **name, char **descr, char **inf_link,
     boolean_t *third_party);
 static int ai_get_ddu_dev_data_values(PyObject *pDDUDevData,
     char **dev_type, char **descr);
 static void ai_du_process_manual_pkg(py_state_t *py_state_p,
-    PyObject *pPackageList, char *location, char *type, char *name,
+    PyObject *pPackageList, char *origin, char *type, char *name,
     char *noinstall);
 static void ai_du_process_manual_pkg_names(py_state_t *py_state_p,
-    PyObject *pPackageList, path_t *path_p, char *location, char *type,
+    PyObject *pPackageList, path_t *path_p, char *origin, char *type,
     char *name);
 static void ai_du_process_manual_pkg_types(py_state_t *py_state_p,
-    PyObject *pPackageList, path_t *path_p, char *location, char *type);
+    PyObject *pPackageList, path_t *path_p, char *origin, char *type);
 static PyObject *ai_du_get_manual_pkg_list(py_state_t *py_state_p,
     path_t *path_p);
 static PyObject *ai_du_get_searched_pkg_list(py_state_t *py_state_p,
@@ -446,22 +447,22 @@
 
 /*
  * ai_new_ddu_package_object:
- * Create a new ddu_package_object of given type, name and location.
+ * Create a new ddu_package_object of given type, name and origin.
  *
  * Arguments:
  *   py_state_p: Initialized py_state_t object.
  *   type: type of package.
  *   name: name of package. (Not used by all types of packages.)
- *   location: directory of where package is located.
+ *   origin: directory of where package is located.
  *
  * Returns:
  *   Success: A new python ddu_package_object object of the given
- *	type/name/location.
+ *	type/name/origin.
  *   Failure: NULL
  */
 static PyObject *
 ai_new_ddu_package_object(py_state_t *py_state_p,
-    char *type, char *name, char *location)
+    char *type, char *name, char *origin)
 /*
  * Construct and return a new python ddu_package_object based on arguments.
  * Assumes auto_ddu_lib_init() has been called.
@@ -484,7 +485,7 @@
 		PyObject *pArgs = PyTuple_New(3);
 		PyTuple_SetItem(pArgs, 0, PyString_FromString(type));
 		PyTuple_SetItem(pArgs, 1, PyString_FromString(name));
-		PyTuple_SetItem(pArgs, 2, PyString_FromString(location));
+		PyTuple_SetItem(pArgs, 2, PyString_FromString(origin));
 
 		/* Call ddu_package_object constructor. */
 		pRet = PyObject_CallObject(pFunc, pArgs);
@@ -519,7 +520,7 @@
  *	ddu_package_object;  not verified.
  *   type: char string pointer returned filled in with "pkg_type" field.
  *	Not processed if NULL.
- *   location: char string pointer returned filled in with "pkg_location" field.
+ *   origin: char string pointer returned filled in with "pkg_location" field.
  *	Not processed if NULL.
  *   name: char string pointer returned filled in with "pkg_name" field.
  *	Not processed if NULL.
@@ -537,7 +538,7 @@
  */
 static int
 ai_get_ddu_package_object_values(PyObject *pDDUPackageObject,
-    char **type, char **location, char **name, char **descr, char **inf_link,
+    char **type, char **origin, char **name, char **descr, char **inf_link,
     boolean_t *third_party)
 {
 	PyObject *pValue;
@@ -553,7 +554,7 @@
 		*type = PyString_AsString(pValue);
 	}
 
-	if (location != NULL) {
+	if (origin != NULL) {
 		pValue = PyObject_GetAttrString(pDDUPackageObject,
 		    "pkg_location");
 		if (pValue == NULL) {
@@ -562,7 +563,7 @@
 			    "no ddu_package_object pkg_location field.\n");
 			return (AUTO_INSTALL_FAILURE);
 		}
-		*location = PyString_AsString(pValue);
+		*origin = PyString_AsString(pValue);
 	}
 
 	if (name != NULL) {
@@ -674,7 +675,7 @@
  * Arguments:
  *   py_state_p: Initialized py_state_t object.
  *   pPackageList: List of packages to append the new ddu_package_object to.
- *   location: directory of where package is located.
+ *   origin: directory of where package is located.
  *   type: type of package.
  *   name: name of package.
  *   noinstall: boolean whether package is to be installed only to booted
@@ -686,33 +687,36 @@
  */
 static void
 ai_du_process_manual_pkg(py_state_t *py_state_p, PyObject *pPackageList,
-    char *location, char *type, char *name, char *noinstall)
+    char *origin, char *type, char *name, char *noinstall)
 {
 	PyObject *pDDUPackageObject;
 	PyObject *pTuple;
 
 	auto_log_print(gettext("Found manifest entry for package:\n"));
 	if (name != empty_string) {
-		auto_log_print(gettext("  type:%s, location:%s, name:%s\n"),
-		    type, location, name);
+		auto_log_print(gettext("  type:%s, origin:%s, name:%s\n"),
+		    type, origin, name);
 	} else {
-		auto_log_print(gettext("  type:%s, location:%s\n"),
-		    type, location);
+		auto_log_print(gettext("  type:%s, origin:%s\n"),
+		    type, origin);
 	}
 	if (strcmp(noinstall, "true") == 0) {
 		auto_log_print(gettext("    Package to be "
 		    "installed only in current booted environment.\n"));
+	} else {
+		auto_log_print(gettext("    Package to be "
+		    "installed in current booted environment and target.\n"));
 	}
 
 	/* Initialize a new ddu_package_object object */
 	pDDUPackageObject = ai_new_ddu_package_object(py_state_p,
-	    type, name, location);
+	    type, name, origin);
 
 	if (pDDUPackageObject == NULL) {
 		auto_debug_print(AUTO_DBGLVL_ERR,
-		    "ai_du_get_pkg_list: <ai_add_drivers> error:\n"
+		    "ai_du_get_pkg_list: <add_drivers> error:\n"
 		    "Error creating new package object for "
-		    "location %s %s\n", location, name);
+		    "origin %s %s\n", origin, name);
 		return;
 	}
 
@@ -731,13 +735,13 @@
 
 /*
  * ai_du_process_manual_pkg_names:
- * Do any processing of packages for which unique location, type and name are
+ * Do any processing of packages for which unique origin, type and name are
  *	known.
  *
  * Arguments:
  *   py_state_p: Initialized py_state_t object.
  *   pPackageList: List of packages to append the new ddu_package_object to.
- *   location: directory of where package is located.
+ *   origin: directory of where package is located.
  *   type: type of package.
  *   name: name of package.
  *
@@ -747,69 +751,88 @@
  */
 static void
 ai_du_process_manual_pkg_names(py_state_t *py_state_p, PyObject *pPackageList,
-    path_t *path_p, char *location, char *type, char *name)
+    path_t *path_p, char *origin, char *type, char *name)
 {
-	char **noinstalls;
-	int noinstlen;
+	char **actions;
+	int actions_len;
 	char *nodespec;
 
-	/* Process "noinstall" entries. */
+	/* Get the action attribute. */
 
 	/* Search is different depending on whether a name is specified. */
 	if (name == empty_string) {
-		nodespec = NOINSTALL_NONAME_NODEPATH;
+		nodespec = ACTION_NONAME_NODEPATH;
 	} else {
-		nodespec = NOINSTALL_YESNAME_NODEPATH;
+		nodespec = ACTION_YESNAME_NODEPATH;
 	}
 
 	if (snprintf(path_p->post_prefix_start, path_p->post_prefix_len,
-	    nodespec, location, type, name) >= path_p->post_prefix_len) {
+	    nodespec, origin, type, name) >= path_p->post_prefix_len) {
 		auto_debug_print(AUTO_DBGLVL_ERR, "ai_du_get_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "noinstall path buffer overflow for location \"%s\", "
-		    "type \"%s\", name \"%s\"\n", location, type, name);
+		    "<add_drivers> manifest error:\n"
+		    "action path buffer overflow for origin \"%s\", "
+		    "type \"%s\", name \"%s\"\n", origin, type, name);
 		return;
 	}
 
-	noinstalls = ai_get_manifest_values(path_p->path_str, &noinstlen);
+	actions = ai_get_manifest_values(path_p->path_str, &actions_len);
 
-	/* Note: this does not have to be filled in for any location. */
-	if (noinstlen <= 0) {
-		/* Obj pointed to by pPackageList will be modified. */
-		ai_du_process_manual_pkg(py_state_p, pPackageList, location,
-		    type, name, empty_string);
+	/*
+	 * Note: action must be present and must be either "install"
+	 * or "noinstall".
+	 */
+	if (actions_len <= 0) {
+		auto_log_print(gettext("ai_du_get_pkg_list: "
+		    "<add_drivers> manifest error:\n"
+		    "no action value for origin \"%s\", "
+		    "type \"%s\", name \"%s\"\n"), origin, type, name);
+		return;
 
-	} else if (noinstlen > 1) {
+	} else if (actions_len > 1) {
 		auto_log_print(gettext("ai_du_get_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "multiple noinstall values for location \"%s\", "
-		    "type \"%s\", name \"%s\"\n"), location, type, name);
+		    "<add_drivers> manifest error:\n"
+		    "multiple action values for origin \"%s\", "
+		    "type \"%s\", name \"%s\"\n"), origin, type, name);
 		return;
 
-	} else if ((strcmp(noinstalls[0], "true") != 0) &&
-	    (strcmp(noinstalls[0], "false") != 0)) {
+	} else if (strcmp(actions[0], "install") == 0) {
+		/*
+		 * If action="install" then call ai_du_process_manual_pkg with
+		 * noinstall param set to empty_string, which means pkg will be
+		 * installed in both boot env and target.
+		 */
+
+		/* Obj pointed to by pPackageList will be modified. */
+		ai_du_process_manual_pkg(py_state_p, pPackageList, origin,
+		    type, name, empty_string);
+	} else if (strcmp(actions[0], "noinstall") == 0) {
+		/*
+		 * If action="noinstall" then call ai_du_process_manual_pkg with
+		 * noinstall param set to "true", which means pkg will only be
+		 * installed in both boot env and not in target.
+		 */
+		/* Obj pointed to by pPackageList will be modified. */
+		ai_du_process_manual_pkg(py_state_p, pPackageList, origin,
+		    type, name, "true");
+	} else {
 		auto_log_print(gettext("ai_du_get_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "non-boolean noinstall value for location \"%s\", "
-		    "type \"%s\", name \"%s\"\n"), location, type, name);
+		    "<add_drivers> manifest error:\n"
+		    "action must be install or noinstall for origin \"%s\", "
+		    "type \"%s\", name \"%s\"\n"), origin, type, name);
 		return;
 
-	} else {
-		/* Obj pointed to by pPackageList will be modified. */
-		ai_du_process_manual_pkg(py_state_p, pPackageList, location,
-		    type, name, noinstalls[0]);
 	}
-	ai_free_manifest_values(noinstalls);
+	ai_free_manifest_values(actions);
 }
 
 /*
  * ai_du_process_manual_pkg_types:
- * Do any processing of packages for which unique location and type are known.
+ * Do any processing of packages for which unique origin and type are known.
  *
  * Arguments:
  *   py_state_p: Initialized py_state_t object.
  *   pPackageList: List of packages to append the new ddu_package_object to.
- *   location: directory of where package is located.
+ *   origin: directory of where package is located.
  *   type: type of package.
  *
  * Returns: None
@@ -818,7 +841,7 @@
  */
 static void
 ai_du_process_manual_pkg_types(py_state_t *py_state_p, PyObject *pPackageList,
-    path_t *path_p, char *location, char *type)
+    path_t *path_p, char *origin, char *type)
 {
 	char **names;
 	char **uniq_names;
@@ -829,20 +852,20 @@
 	    (strcmp(type, "SVR4") != 0) &&
 	    (strcmp(type, "DU") != 0)) {
 		auto_log_print(gettext("ai_du_get_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "invalid type %s given for location %s\n"),
-		    type, location);
+		    "<add_drivers> manifest error:\n"
+		    "invalid type %s given for origin %s\n"),
+		    type, origin);
 		return;
 	}
 
-	/* Get all names assocated with type and location. */
+	/* Get all names assocated with type and origin. */
 
 	if (snprintf(path_p->post_prefix_start, path_p->post_prefix_len,
-	    NAME_NODEPATH, location, type) >= path_p->post_prefix_len) {
+	    NAME_NODEPATH, origin, type) >= path_p->post_prefix_len) {
 		auto_debug_print(AUTO_DBGLVL_ERR, "ai_du_get_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "name path buffer overflow for location "
-		    "%s, type %s\n", location, type);
+		    "<add_drivers> manifest error:\n"
+		    "name path buffer overflow for origin "
+		    "%s, type %s\n", origin, type);
 		return;
 	}
 
@@ -855,32 +878,35 @@
 	if (strcmp(type, "SVR4") != 0) {
 		if (namelen > 0) {
 			auto_log_print(gettext(
-			    "ai_du_get_pkg_list: <ai_add_drivers> "
+			    "ai_du_get_pkg_list: <add_drivers> "
 			    "manifest error:\n"
-			    "name given to P5I or DU bundle at "
-			    "location %s\n"), location);
+			    "name given to P5I or DU pkg spec at "
+			    "origin %s\n"), origin);
 			return;
 		} else {
 			/* Obj pointed to by pPackageList will be modified. */
 			ai_du_process_manual_pkg_names(py_state_p, pPackageList,
-			    path_p, location, type, empty_string);
+			    path_p, origin, type, empty_string);
 		}
 
-	/* There must be at least one "name" entry per bundle for SVR4 type. */
+	/*
+	 * There must be at least one "name" entry per pkg spec
+	 * for SVR4 type.
+	 */
 	} else if (namelen <= 0) {
 		auto_log_print(gettext("ai_du_get_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "no name given for SVR4 bundle at location %s, type %s\n"),
-		    location, type);
+		    "<add_drivers> manifest error:\n"
+		    "no name given for SVR4 pkg spec at origin %s, type %s\n"),
+		    origin, type);
 		return;
 
 	} else {
-		/* Process each location/type/name entry. */
+		/* Process each origin/type/name entry. */
 		for (k = 0; k < namelen; k++) {
 
 			/* Obj pointed to by pPackageList will be modified. */
 			ai_du_process_manual_pkg_names(py_state_p, pPackageList,
-			    path_p, location, type, names[k]);
+			    path_p, origin, type, names[k]);
 		}
 
 	}
@@ -889,19 +915,27 @@
 
 /*
  * ai_du_get_manual_pkg_list:
- * Read the AI ai_manifest.xml file and process the <bundles> under the
- * <ai_add_drivers> section.  A <bundle> is a manual specification of a package
+ * Read the AI ai.xml Manifest file and process the <software> under the
+ * <add_drivers> section.  A <software> is a manual specification of a package
  * to install.  Do error checking of the manifest as necessary, as this function
  * reads the manifest before it is validated against a schema.
  *
  * Validates syntax and processes the following from the manifest:
- *	<ai_add_drivers>
- *		<bundle type="type" location="location" name="name"
- *		    noinstall="true or false" />
- *	</ai_add_drivers>
+ *	<add_drivers>
+ *		<software>
+ *			<source>
+ *				<publisher>
+ *					<origin name="origin"/>
+ *				</publisher>
+ *			</source>
+ *			<software_data type="type" action="noinstall">
+ *				<name>"name"</name>
+ *			</software_data>
+ *		</software>
+ *	</add_drivers>
  *
  *	type can be "SVR4", "P5I" or "DU".
- *	name not required if type is "P5I"
+ *	name not allowed if type is "P5I" or "DU"
  *
  * Arguments:
  *   py_state_p: Initialized py_state_t object.
@@ -920,58 +954,58 @@
 ai_du_get_manual_pkg_list(py_state_t *py_state_p, path_t *path_p)
 {
 	PyObject *pPackageList = NULL;
-	char **uniq_locns = NULL;
+	char **uniq_origins = NULL;
 	char **types = NULL;
-	int locnlen, typelen;
-	char **locations;
+	int origin_len, typelen;
+	char **origins;
 	char **uniq_types;
-	int num_bundles;
+	int num_pkg_specs;
 	int i, j;
 
 	/* Read manifest for specific package requests. */
 
-	/* Get the number of bundle entries. */
-	if (strlcpy(path_p->post_prefix_start, BUNDLE_NODEPATH,
+	/* Get the number of pkg spec entries. */
+	if (strlcpy(path_p->post_prefix_start, PKGSPEC_NODEPATH,
 	    path_p->post_prefix_len) > path_p->post_prefix_len) {
 		auto_debug_print(AUTO_DBGLVL_ERR,
-		    "ai_du_get_pkg_list: bundle path buffer overflow\n");
+		    "ai_du_get_pkg_list: pkg spec path buffer overflow\n");
 		return (NULL);
 	}
 
-	/* Use "locations" like a dummy here.  Interest only in num_bundles. */
-	locations = ai_get_manifest_values(path_p->path_str, &num_bundles);
-	ai_free_manifest_values(locations);
+	/* Use "origins" like a dummy here.  Interest only in num_pkg_specs. */
+	origins = ai_get_manifest_values(path_p->path_str, &num_pkg_specs);
+	ai_free_manifest_values(origins);
 
-	/* No bundles.  Return an empty list. */
-	if (num_bundles <= 0) {
+	/* No pkg specs.  Return an empty list. */
+	if (num_pkg_specs <= 0) {
 		return (PyList_New(0));
 	}
 
-	/* Retrieve a list of all specific package request locations. */
-	if (strlcpy(path_p->post_prefix_start, LOCN_NODEPATH,
+	/* Retrieve a list of all specific package request origins. */
+	if (strlcpy(path_p->post_prefix_start, ORIGIN_NODEPATH,
 	    path_p->post_prefix_len) > path_p->post_prefix_len) {
 		auto_debug_print(AUTO_DBGLVL_ERR,
-		    "ai_du_get_pkg_list: location path buffer overflow\n");
+		    "ai_du_get_pkg_list: origin path buffer overflow\n");
 		return (NULL);
 	}
 
-	/* Get real locations list here for use below. */
-	locations = ai_get_manifest_values(path_p->path_str, &locnlen);
+	/* Get real origins list here for use below. */
+	origins = ai_get_manifest_values(path_p->path_str, &origin_len);
 
 	/*
-	 * Not a perfect test to validate bundles and locations in manifest,
+	 * Not a perfect test to validate pkg specs and origins in manifest,
 	 * but it will do...
 	 */
-	if (locnlen != num_bundles) {
+	if (origin_len != num_pkg_specs) {
 		auto_debug_print(AUTO_DBGLVL_ERR,
-		    "ai_du_get_pkg_list: <ai_add_drivers> manifest error:\n"
-		    "There is not a 1-1 location-bundle mapping.\n");
+		    "ai_du_get_pkg_list: <add_drivers> manifest error:\n"
+		    "There is not a 1-1 origin to pkg spec mapping.\n");
 		return (NULL);
 	}
 
-	uniq_locns = ai_uniq_manifest_values(locations, &locnlen);
-	ai_free_manifest_values(locations);
-	locations = uniq_locns;
+	uniq_origins = ai_uniq_manifest_values(origins, &origin_len);
+	ai_free_manifest_values(origins);
+	origins = uniq_origins;
 
 	/*
 	 * Initialize a zero-length list.
@@ -980,29 +1014,29 @@
 	pPackageList = PyList_New(0);
 
 	/*
-	 * For each location, get types.  Note it is possible for there to be
-	 * more than one type at a location.  There can also be more than one
-	 * item of a given type at a location.
+	 * For each origin, get types.  Note it is possible for there to be
+	 * more than one type at a origin.  There can also be more than one
+	 * item of a given type at a origin.
 	 */
-	for (i = 0; i < locnlen; i++) {
+	for (i = 0; i < origin_len; i++) {
 
 		/* Process "type" entries. */
 
 		if (snprintf(path_p->post_prefix_start, path_p->post_prefix_len,
-		    TYPE_NODEPATH, locations[i]) >= path_p->post_prefix_len) {
+		    TYPE_NODEPATH, origins[i]) >= path_p->post_prefix_len) {
 			auto_debug_print(AUTO_DBGLVL_ERR,
 			    "ai_du_get_pkg_list: "
-			    "<ai_add_drivers> manifest error:\n"
-			    "type path buffer overflow for location %s\n",
-			    locations[i]);
+			    "<add_drivers> manifest error:\n"
+			    "type path buffer overflow for origin %s\n",
+			    origins[i]);
 			continue;
 		}
 
 		types = ai_get_manifest_values(path_p->path_str, &typelen);
 		if (typelen <= 0) {
 			auto_log_print(gettext("ai_du_get_pkg_list: "
-			    "<ai_add_drivers> manifest error:\n"
-			    "no type given for location %s\n"), locations[i]);
+			    "<add_drivers> manifest error:\n"
+			    "no type given for origin %s\n"), origins[i]);
 			continue;
 		}
 
@@ -1010,35 +1044,40 @@
 		ai_free_manifest_values(types);
 		types = uniq_types;
 
-		/* Loop for all types found at this location... */
+		/* Loop for all types found at this origin... */
 		for (j = 0; j < typelen; j++) {
 
 			/* Obj pointed to by pPackageList will be modified. */
 			ai_du_process_manual_pkg_types(py_state_p, pPackageList,
-			    path_p, locations[i], types[j]);
+			    path_p, origins[i], types[j]);
 		}
 	}
-	ai_free_manifest_values(locations);
+	ai_free_manifest_values(origins);
 	ai_free_manifest_values(types);
 	return (pPackageList);
 }
 
 /*
  * ai_du_get_searched_pkg_list:
- * Read the AI ai_manifest.xml file and process the <searchall> tag under the
- * <ai_add_drivers> section.  Do the needful to scan for missing devices and to
+ * Read the AI ai.xml Manifest file and process the <search_all> tag under the
+ * <add_drivers> section.  Do the needful to scan for missing devices and to
  * perform package searches and installs for missing drivers.  Do error
  * checking of the manifest as necessary, as this function reads the manifest
  * before it is validated against a schema.
  *
  * Validates syntax and processes the following from the manifest:
- *	<ai_add_drivers>
- *		<searchall publisher="pub" location="location"
- *		    addall="true or false"/>
- *	</ai_add_drivers>
+ *	<add_drivers>
+ *		<search_all addall="false">
+ *			<source>
+ *				<publisher name="publisher">
+ *					<origin name="origin"/>
+ *				</publisher>
+ *			</source>
+ *		</search_all>
+ *	</add_drivers>
  *
- *	publisher and location are both optional, but both must be specified
- *	    together.
+ *  publisher and origin are both optional, but if one is specified then the
+ *  other must also be specified.
  *	addall is optional.  Defaults to "false" if not specified.
  *
  * Arguments:
@@ -1063,10 +1102,10 @@
 	PyObject *pRepoTupleList;
 	int len, sublen;
 	PyObject *pSearchRepoList = NULL;
-	char *search_locn, *search_pub;
+	char *search_origin, *search_pub;
 	PyObject *py_search_addall = NULL;
 	char **searches = NULL;
-	char **search_locns = NULL;
+	char **search_origins = NULL;
 	char **search_pubs = NULL;
 	char **search_addalls = NULL;
 	Py_ssize_t i, listlen;
@@ -1088,7 +1127,7 @@
 	ai_free_manifest_values(searches);
 	if (len > 1) {
 		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
-		    "too many <searchall> entries in manifest\n"));
+		    "too many <search_all> entries in manifest\n"));
 		return (NULL);
 
 	} else if (len <= 0) {
@@ -1114,12 +1153,12 @@
 		return (pPackageList);
 	}
 
-	/* Get repo location, if specified. */
+	/* Get repo origin, if specified. */
 
-	if (strlcpy(path_p->post_prefix_start, SEARCH_LOCN_NODEPATH,
+	if (strlcpy(path_p->post_prefix_start, SEARCH_ORIGIN_NODEPATH,
 	    path_p->post_prefix_len) > path_p->post_prefix_len) {
 		auto_debug_print(AUTO_DBGLVL_ERR,
-		    "ai_du_get_searched_pkg_list: search repo location path "
+		    "ai_du_get_searched_pkg_list: search repo origin path "
 		    "buffer overflow.\n");
 		return (NULL);
 	}
@@ -1127,21 +1166,21 @@
 	auto_log_print(gettext("ai_du_get_searched_pkg_list: Querying manifest "
 	    "for explicit repo for getting missing driver packages...\n"));
 
-	search_locns = ai_get_manifest_values(path_p->path_str, &sublen);
+	search_origins = ai_get_manifest_values(path_p->path_str, &sublen);
 	if (sublen == 1) {
-		search_locn = search_locns[0];
+		search_origin = search_origins[0];
 	} else if (sublen <= 0) {
-		search_locn = empty_string;
+		search_origin = empty_string;
 	} else {
 		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "Only one <searchall> entry allowed\n"));
+		    "<add_drivers> manifest error:\n"
+		    "Only one origin allowed per <search_all> entry.\n"));
 		goto done;
 	}
 
 	/* Get repo publisher, if specified. */
 
-	if (strlcpy(path_p->post_prefix_start, SEARCH_PUB_NODEPATH,
+	if (strlcpy(path_p->post_prefix_start, SEARCH_PUBNAME_NODEPATH,
 	    path_p->post_prefix_len) > path_p->post_prefix_len) {
 		auto_debug_print(AUTO_DBGLVL_ERR,
 		    "ai_du_get_searched_pkg_list: search repo publisher path "
@@ -1156,34 +1195,34 @@
 		search_pub = empty_string;
 	} else {
 		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "Only one publisher allowed for a <searchall> entry\n"));
+		    "<add_drivers> manifest error:\n"
+		    "Only one publisher allowed for a <search_all> entry\n"));
 		goto done;
 	}
 
 	/* Can't have one without the other. */
 	if ((search_pub == empty_string) ^
-	    (search_locn == empty_string)) {
+	    (search_origin == empty_string)) {
 		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "search repo location and "
+		    "<add_drivers> manifest error:\n"
+		    "search repo origin and "
 		    "publisher must be specified together.\n"));
 		goto done;
 	}
 
 	/*
-	 * if publisher and location provided, create tuple from them and
+	 * if publisher and origin provided, create tuple from them and
 	 * build a repo list from it.
 	 */
 	if (search_pub != empty_string) {
 
 		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
-		    "Found repo in manifest: publisher:%s, location:%s\n"),
-		    search_pub, search_locn);
+		    "Found repo in manifest: publisher:%s, origin:%s\n"),
+		    search_pub, search_origin);
 
 		pTuple = PyTuple_New(2);
 		PyTuple_SetItem(pTuple, 0, PyString_FromString(search_pub));
-		PyTuple_SetItem(pTuple, 1, PyString_FromString(search_locn));
+		PyTuple_SetItem(pTuple, 1, PyString_FromString(search_origin));
 		pRepoTupleList = PyList_New(0);
 		PyList_Append(pRepoTupleList, pTuple);
 		pSearchRepoList = ai_call_ddu_build_repo_list(py_state_p,
@@ -1202,7 +1241,7 @@
 
 		auto_debug_print(AUTO_DBGLVL_INFO,
 		    "ai_du_get_searched_pkg_list: "
-		    "No <searchall> repo found in manifest\n");
+		    "No <search_all> repo found in manifest\n");
 
 		pSearchRepoList = PyList_New(0);
 	}
@@ -1224,8 +1263,8 @@
 	    ((strcmp(search_addalls[0], "true") != 0) &&
 	    (strcmp(search_addalls[0], "false") != 0)))) {
 		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
-		    "<ai_add_drivers> manifest error:\n"
-		    "invalid addall value for <searchall> entry\n"));
+		    "<add_drivers> manifest error:\n"
+		    "invalid addall value for <search_all> entry\n"));
 		goto done;
 
 	/* Default to false if not provided. */
@@ -1307,7 +1346,7 @@
 
 done:
 	/* Cleanup time, whether an error occured or not. */
-	ai_free_manifest_values(search_locns);
+	ai_free_manifest_values(search_origins);
 	ai_free_manifest_values(search_pubs);
 	Py_XDECREF(py_search_addall);
 	Py_XDECREF(pSearchRepoList);
@@ -1365,30 +1404,30 @@
 		PyObject *pThirdPartyOK = PyTuple_GetItem(pTuple, 1);
 		PyObject *pNoInstall = PyTuple_GetItem(pTuple, 2);
 		char *type = empty_string;
-		char *location = empty_string;
+		char *origin = empty_string;
 		char *name = empty_string;
 		char *descr = empty_string;
 		char *inf_link = empty_string;
 		boolean_t third_party;
 
 		if (ai_get_ddu_package_object_values(pDDUPackageObject,
-		    &type, &location, &name, &descr, &inf_link, &third_party) !=
+		    &type, &origin, &name, &descr, &inf_link, &third_party) !=
 		    AUTO_INSTALL_SUCCESS) {
 			auto_debug_print(AUTO_DBGLVL_ERR,
 			    "ai_du_install_packages: Error extracting package "
 			    "information for ddu_package_object.\n");
-			type = location = name = descr = inf_link =
+			type = origin = name = descr = inf_link =
 			    empty_string;
 			third_party = B_FALSE;
 		} else {
 			if (strcmp(name, empty_string) == 0) {
 				auto_log_print(gettext(
-				    "  %s package at location:%s\n"),
-				    type, location);
+				    "  %s package at origin:%s\n"),
+				    type, origin);
 			} else {
 				auto_log_print(gettext(
-				    "  %s package at location:%s, name:%s\n"),
-				    type, location, name);
+				    "  %s package at origin:%s, name:%s\n"),
+				    type, origin, name);
 			}
 		}
 
@@ -1416,7 +1455,7 @@
 		}
 
 		/* Handle uninstallable package objects. */
-		if (strcmp(location, empty_string) == 0) {
+		if (strcmp(origin, empty_string) == 0) {
 			if (strcmp(inf_link, empty_string) == 0) {
 				auto_log_print(gettext(
 				    "ai_du_install_packages: Package not "
@@ -1568,16 +1607,16 @@
 
 /*
  * ai_du_get_and_install:
- * Query the manifest for the entire <ai_add_drivers> section, and add packages
+ * Query the manifest for the entire <add_drivers> section, and add packages
  * accordingly.  Add packages to install_root.  If a package has its noinstall
  * flag set and the honor_noinstall argument is set, skip adding that package.
  * Save the list of packages to install to the module global py_pkg_list, so
  * that the same list of packages can be installed to a different target with
  * ai_du_install().
  *
- * Install all explicitly-stated packages first.  Then do <searchall> last.
+ * Install all explicitly-stated packages first.  Then do <search_all> last.
  * This is to handle any explicit requests for matching a special driver to a
- * device, before <searchall> finds the first available one.
+ * device, before <search_all> finds the first available one.
  *
  * Assumes ai_create_manifest_image() has set up the manifest data.
  * Does not assume any data has been verified though.
@@ -1635,19 +1674,19 @@
 	py_pkg_list = PyList_New(0);
 
 	/*
-	 * See if the manifest has at least one bundle or searchall entry.
+	 * See if the manifest has at least one pkg spec or searchall entry.
 	 * If not, just return success (e.g. no-op).
 	 */
 
-	/* Get the number of bundle entries. */
-	if (strlcpy(path.post_prefix_start, BUNDLE_NODEPATH,
+	/* Get the number of pkg spec entries. */
+	if (strlcpy(path.post_prefix_start, PKGSPEC_NODEPATH,
 	    path.post_prefix_len) > path.post_prefix_len) {
 		auto_debug_print(AUTO_DBGLVL_ERR,
-		    "ai_du_get_and_install: bundle path buffer overflow\n");
+		    "ai_du_get_and_install: pkg spec path buffer overflow\n");
 		return (-1);
 	}
 
-	/* Get number of bundle entries in the manifest. */
+	/* Get number of pkg spec entries in the manifest. */
 	dummy_list = ai_get_manifest_values(path.path_str, &num_entries);
 	ai_free_manifest_values(dummy_list);
 
@@ -1689,7 +1728,7 @@
 	if (manual_pkg_list == NULL) {
 		auto_debug_print(AUTO_DBGLVL_ERR,
 		    "ai_du_get_and_install: "
-		    "Error getting package <bundle>.\n");
+		    "Error getting package specification.\n");
 		rval = -1;
 		/* Keep going.  Don't abort. */
 	} else {
@@ -1700,7 +1739,7 @@
 			    &num_pkgs_installed) != AUTO_INSTALL_SUCCESS) {
 				auto_debug_print(AUTO_DBGLVL_ERR,
 				    "ai_du_get_and_install: Error installing "
-				    "at least one package <bundle>.\n");
+				    "at least one package.\n");
 				rval = -1;
 				/* Keep going.  Don't abort. */
 			}
@@ -1724,7 +1763,7 @@
 				auto_debug_print(AUTO_DBGLVL_ERR,
 				    "ai_du_get_and_install: Error installing "
 				    "at least one searched package "
-				    "for <searchall>.\n");
+				    "for <search_all>.\n");
 				rval = -1;
 				/* Keep going.  Don't abort. */
 			}
@@ -1774,7 +1813,7 @@
  * This routine assumes the py_pkg_list was set up via a prior call to
  * ai_du_get_and_install_packages().
  *
- * The availability and location of all packages to be installed is assumed the
+ * The availability and origin of all packages to be installed is assumed the
  * same as when the py_pkg_list was built (i.e. the most recent call to
  * ai_du_get_and_install()).
  *
--- a/usr/src/cmd/auto-install/auto_install.c	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/auto_install.c	Fri Aug 20 11:31:18 2010 -0600
@@ -204,15 +204,15 @@
 }
 
 /*
- * This function splits the file that is passed to AI manifest and SC manifest
+ * This function splits the passed in file into AI manifest and SC manifest
  *
  * Input:
- * char *input_file	- The file contains AI manifest (relax NG schema) and
- *			  SC manifest (enhanced SMF profile DTD schema)
+ * char *input_file	- AI manifest file with embedded SC manifest
+ *
  * Output:
- * char *ai_manifest	- Writes the AI manifest portion of the input on this
+ * char *ai_manifest	- Writes the AI manifest portion of the input to this
  *			  file name
- * char *sc_manifest	- Writes the SC manifest portion of the input on this
+ * char *sc_manifest	- Writes the SC manifest portion of the input to this
  *			  file name
  * Returns:
  * AUTO_VALID_MANIFEST (0)	- If the operation is successful
@@ -224,7 +224,7 @@
 	FILE		*ifp;	/* Input file */
 	FILE		*aifp;	/* AI manifest */
 	FILE		*scfp;	/* SC manifest */
-	boolean_t	writing_ai_manifest = B_FALSE;
+	boolean_t	writing_ai_manifest = B_TRUE;
 	boolean_t	writing_sc_manifest = B_FALSE;
 	char		buf[BUFSIZ];
 
@@ -267,16 +267,21 @@
 	while (fgets(buf, sizeof (buf), ifp) != NULL) {
 
 		/*
-		 * The AI manifest begins with <ai_manifest and
-		 * ends with the line "</ai_manifest>"
 		 * The SC manifest begins with <?xml version='1.0'?> and
-		 * ends with the line "</service_bundle>"
-		 *
+		 * ends with the line "</service_bundle>". It is embedded
+		 * in the input file between <sc_embedded_manifest ...>
+		 * and  </sc_embedded_manifest>.
 		 */
-		if (strstr(buf, AI_MANIFEST_BEGIN_MARKER) != NULL) {
+		if (strstr(buf, SC_EMBEDDED_BEGIN_MARKER) != NULL) {
+			writing_ai_manifest = B_FALSE;
+			continue;
+		}
+		if (strstr(buf, SC_EMBEDDED_END_MARKER) != NULL) {
 			writing_ai_manifest = B_TRUE;
+			continue;
 		}
-		if (strstr(buf, SC_MANIFEST_BEGIN_MARKER) != NULL) {
+		if ((strstr(buf, SC_MANIFEST_BEGIN_MARKER) != NULL) &&
+		    (! writing_ai_manifest)) {
 			writing_sc_manifest = B_TRUE;
 
 			/*
@@ -292,9 +297,6 @@
 			continue;
 		}
 		if (writing_ai_manifest) {
-			if (strstr(buf, AI_MANIFEST_END_MARKER) != NULL) {
-				writing_ai_manifest = B_FALSE;
-			}
 			fputs(buf, aifp);
 			continue;
 		} else if (writing_sc_manifest) {
@@ -386,22 +388,17 @@
 		    AIM_PACKAGE_INSTALL_NAME);
 
 		if (package_list == NULL) {
-			auto_debug_print(AUTO_DBGLVL_INFO,
-			    "Tag %s not available, %s will be tried\n",
-			    AIM_PACKAGE_INSTALL_NAME,
-			    AIM_OLD_PACKAGE_INSTALL_NAME);
+			/* If no package list given, use default */
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "No install package list given, using default\n");
 
-			package_list = ai_get_manifest_packages(&num_packages,
-			    AIM_OLD_PACKAGE_INSTALL_NAME);
-		}
-
-		if (package_list == NULL) {
-			auto_debug_print(AUTO_DBGLVL_ERR,
-			    "Couldn't obtain list of packages to be "
-			    "installed\n");
-
-			(void) fclose(fp);
-			return (AUTO_INSTALL_FAILURE);
+			num_packages = 4;
+			package_list = malloc((num_packages + 1) * sizeof (char *));
+			package_list[0] = strdup("pkg:/SUNWcsd");
+			package_list[1] = strdup("pkg:/SUNWcs");
+			package_list[2] = strdup("pkg:/babel_install");
+			package_list[3] = strdup("pkg:/entire");
+			package_list[4] = NULL;
 		}
 
 		auto_log_print(gettext(
@@ -750,7 +747,7 @@
 
 /*
  * Install the target based on the criteria specified in
- * the ai_manifest.xml.
+ * ai.xml.
  *
  * NOTE: ai_validate_manifest() MUST have been called prior
  * to calling this function.
@@ -1421,7 +1418,7 @@
 /*
  * Install the target based on the specified diskname
  * or if no diskname is specified, install it based on
- * the criteria specified in the ai_manifest.xml.
+ * the criteria specified in ai.xml.
  *
  * Returns
  *	AUTO_INSTALL_SUCCESS on a successful install
@@ -1808,13 +1805,9 @@
 		char	*ai_auto_reboot;
 
 		/*
-		 * We are passed in a combined AI and SC manifest.
-		 * Before we doing anything meaningful, they must
-		 * be separated since they're in two different
-		 * formats
-		 *
-		 * The AI manifest is in RelaxNG format whereas the
-		 * SC manifest is in a DTD format.
+		 * We are passed in an AI manifest with an embedded
+		 * SC manifest, both in DTD format. We want to
+		 * separate them.
 		 */
 		if (auto_split_manifests(profile, AI_MANIFEST_FILE,
 		    SC_MANIFEST_FILE) != AUTO_VALID_MANIFEST) {
@@ -1838,11 +1831,20 @@
 			exit(AI_EXIT_FAILURE_AIM);
 		}
 
+		if (ai_setup_manifest_image() == AUTO_VALID_MANIFEST) {
+			auto_log_print(gettext(
+			    "%s manifest setup and validated\n"), profile);
+		} else {
+			char *setup_err = gettext("Auto install failed. Error "
+			    "setting up and validating manifest %s\n");
+			auto_log_print(setup_err, profile);
+			(void) fprintf(stderr, setup_err, profile);
+			exit(AI_EXIT_FAILURE_AIM);
+		}
+
 		/*
 		 * Install any drivers required for installation, in the
-		 * booted environment.  This must be done before semantic
-		 * validation, since this may add required devices which
-		 * are needed to pass validation.
+		 * booted environment.
 		 */
 
 		/*
@@ -1871,17 +1873,6 @@
 			(void) fprintf(stderr, du_warning);
 		}
 
-		if (ai_setup_manifest_image() == AUTO_VALID_MANIFEST) {
-			auto_log_print(gettext(
-			    "%s manifest setup and validated\n"), profile);
-		} else {
-			char *setup_err = gettext("Auto install failed. Error "
-			    "setting up and validating manifest %s\n");
-			auto_log_print(setup_err, profile);
-			(void) fprintf(stderr, setup_err, profile);
-			exit(AI_EXIT_FAILURE_AIM);
-		}
-
 		diskname[0] = '\0';
 
 		/*
--- a/usr/src/cmd/auto-install/auto_install.h	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/auto_install.h	Fri Aug 20 11:31:18 2010 -0600
@@ -58,10 +58,10 @@
 
 #define	AUTO_VALID_MANIFEST	0
 #define	AUTO_INVALID_MANIFEST	-1
-#define	AI_MANIFEST_BEGIN_MARKER	"<ai_manifest"
-#define	AI_MANIFEST_END_MARKER		"</ai_manifest>"
 #define	SC_MANIFEST_BEGIN_MARKER	"<?xml version='1.0'?>"
 #define	SC_MANIFEST_END_MARKER		"</service_bundle>"
+#define	SC_EMBEDDED_BEGIN_MARKER	"<sc_embedded_manifest"
+#define	SC_EMBEDDED_END_MARKER		"</sc_embedded_manifest>"
 #define	SC_PROPVAL_MARKER		"<propval"
 #define	AUTO_PROPERTY_ROOTPASS		"rootpass"
 #define	AUTO_PROPERTY_TIMEZONE		"timezone"
@@ -82,8 +82,8 @@
  */
 #define	AUTO_REMOVE_PKG_LIST_FILE	"/tmp/remove.pkg.list"
 
-#define	AI_MANIFEST_FILE	"/tmp/ai_manifest.xml"
-#define	AI_MANIFEST_SCHEMA	"/tmp/ai_manifest.rng"
+#define	AI_MANIFEST_FILE	"/tmp/ai.xml"
+#define	AI_MANIFEST_SCHEMA	"/tmp/ai.dtd"
 #define	SC_MANIFEST_FILE	"/tmp/sc_manifest.xml"
 
 /* Script for converting legacy System Configuration manifest to new format */
@@ -98,153 +98,103 @@
 	((units) == AI_SIZE_UNITS_SECTORS ? "sectors": \
 	"(unknown)"))))
 
-/*
- * RNG schema definitions - see ai_manifest.rng
- */
-#define	AIM_TARGET_DEVICE_NAME "ai_manifest/ai_target_device/target_device_name"
-#define	AIM_TARGET_DEVICE_BOOT_DISK "boot_disk"
-#define	AIM_TARGET_DEVICE_SELECT_VOLUME_NAME \
-	"ai_manifest/ai_target_device/target_device_select_volume_name"
-#define	AIM_TARGET_DEVICE_SELECT_DEVICE_ID \
-	"ai_manifest/ai_target_device/target_device_select_id"
-#define	AIM_TARGET_DEVICE_SELECT_DEVICE_PATH \
-	"ai_manifest/ai_target_device/target_device_select_device_path"
-#define	AIM_TARGET_DEVICE_TYPE "ai_manifest/ai_target_device/target_device_type"
-#define	AIM_TARGET_DEVICE_SIZE	\
-	"ai_manifest/ai_target_device/target_device_size"
-#define	AIM_TARGET_DEVICE_VENDOR	\
-	"ai_manifest/ai_target_device/target_device_vendor"
-#define	AIM_TARGET_DEVICE_USE_SOLARIS_PARTITION	\
-	"ai_manifest/ai_target_device/target_device_use_solaris_partition"
-#define	AIM_TARGET_DEVICE_OVERWRITE_ROOT_ZFS_POOL \
-	"ai_manifest/ai_target_device/target_device_overwrite_root_zfs_pool"
-#define	AIM_TARGET_DEVICE_INSTALL_SLICE_NUMBER \
-	"ai_manifest/ai_target_device/target_device_install_slice_number"
-#define	AIM_TARGET_DEVICE_ISCSI_TARGET_NAME \
-	"ai_manifest/ai_target_device/target_device_iscsi_target_name"
-#define	AIM_TARGET_DEVICE_ISCSI_TARGET_IP \
-	"ai_manifest/ai_target_device/target_device_iscsi_target_ip"
-#define	AIM_TARGET_DEVICE_ISCSI_TARGET_LUN \
-	"ai_manifest/ai_target_device/target_device_iscsi_target_lun"
-#define	AIM_TARGET_DEVICE_ISCSI_TARGET_PORT \
-	"ai_manifest/ai_target_device/target_device_iscsi_target_port"
-#define	AIM_TARGET_DEVICE_ISCSI_TARGET_CHAP_NAME \
-	"ai_manifest/ai_target_device/target_device_iscsi_target_chap_name"
-#define	AIM_TARGET_DEVICE_ISCSI_TARGET_CHAP_SECRET \
-	"ai_manifest/ai_target_device/target_device_iscsi_target_chap_secret"
-#define	AIM_TARGET_DEVICE_ISCSI_TARGET_INITIATOR \
-	"ai_manifest/ai_target_device/target_device_iscsi_initiator_name"
-#define	AIM_TARGET_DEVICE_ISCSI_PARAMETER_SOURCE \
-	"ai_manifest/ai_target_device/target_device_iscsi_parameter_source"
-#define	AIM_SWAP_SIZE	\
-	"ai_manifest/ai_swap_device/ai_swap_size"
-#define	AIM_DUMP_SIZE	\
-	"ai_manifest/ai_dump_device/ai_dump_size"
+#define MB_TO_SECTORS	((uint64_t)2048)
+#define GB_TO_MB	((uint64_t)1024)
+#define TB_TO_GB	((uint64_t)1024)
 
-#define	AIM_PARTITION_ACTION	\
-	"ai_manifest/ai_device_partitioning/partition_action"
-#define	AIM_PARTITION_NUMBER	\
-	"ai_manifest/ai_device_partitioning/partition_number"
-#define	AIM_PARTITION_START_SECTOR	\
-	"ai_manifest/ai_device_partitioning/partition_start_sector"
-#define	AIM_PARTITION_SIZE	\
-	"ai_manifest/ai_device_partitioning/partition_size"
-#define	AIM_PARTITION_TYPE	\
-	"ai_manifest/ai_device_partitioning/partition_type"
-#define	AIM_PARTITION_SIZE_UNITS	\
-	"ai_manifest/ai_device_partitioning/partition_size_units"
-#define	AIM_PARTITION_IS_LOGICAL	\
-	"ai_manifest/ai_device_partitioning/partition_is_logical"
-
-#define	AIM_SLICE_ACTION "ai_manifest/ai_device_vtoc_slices/slice_action"
-#define	AIM_SLICE_NUMBER "ai_manifest/ai_device_vtoc_slices/slice_number"
-#define	AIM_SLICE_SIZE "ai_manifest/ai_device_vtoc_slices/slice_size"
-#define	AIM_SLICE_SIZE_UNITS	\
-	"ai_manifest/ai_device_vtoc_slices/slice_size_units"
-#define	AIM_SLICE_ON_EXISTING	\
-	"ai_manifest/ai_device_vtoc_slices/slice_on_existing"
-#define	AIM_AUTO_REBOOT	"ai_manifest/ai_auto_reboot"
-
-#define	AIM_PROXY_URL "ai_manifest/ai_http_proxy/url"
 
 /*
- * There are two tags supported for specifying list
- * of packages to be installed in order to keep
- * backward compatibility
- */
-#define	AIM_OLD_PACKAGE_INSTALL_NAME "ai_manifest/ai_packages/package_name"
-#define	AIM_PACKAGE_INSTALL_NAME "ai_manifest/ai_install_packages/pkg/name"
-
-#define	AIM_PACKAGE_REMOVE_NAME "ai_manifest/ai_uninstall_packages/pkg/name"
-
-/*
- * Define default publisher
+ * DTD schema nodepaths - see ai.dtd
  */
-#define	AIM_IPS_DEFAULT_PUBLISHER_NAME	\
-	"ai_manifest/ai_pkg_repo_default_publisher/main/publisher"
-#define	AIM_IPS_DEFAULT_PUBLISHER_URL	\
-	"ai_manifest/ai_pkg_repo_default_publisher/main/url"
-#define	AIM_IPS_DEFAULT_PUBLISHER_MIRROR	\
-	"ai_manifest/ai_pkg_repo_default_publisher/mirror/url"
-/*
- * define default authority (backward compatibility)
- */
-#define	AIM_IPS_DEFAULT_AUTH_NAME	\
-	"ai_manifest/ai_pkg_repo_default_authority/main/authname"
-#define	AIM_IPS_DEFAULT_AUTH_URL	\
-	"ai_manifest/ai_pkg_repo_default_authority/main/url"
-#define	AIM_IPS_DEFAULT_AUTH_MIRROR	\
-	"ai_manifest/ai_pkg_repo_default_authority/mirror/url"
+#define	AIM_TARGET_DISK_KEYWORD "auto_install/ai_instance/target/target_device/disk/disk_keyword/key"
+#define	AIM_TARGET_DEVICE_NAME "auto_install/ai_instance/target/target_device/disk/disk_name[name_type='ctd']/name"
+#define	AIM_TARGET_DEVICE_BOOT_DISK "boot_disk"
+#define	AIM_TARGET_DEVICE_SELECT_VOLUME_NAME \
+	"auto_install/ai_instance/target/target_device/disk/disk_name[name_type='volid']/name"
+#define	AIM_TARGET_DEVICE_SELECT_DEVICE_ID \
+	"auto_install/ai_instance/target/target_device/disk/disk_name[name_type='devid']/name"
+#define	AIM_TARGET_DEVICE_SELECT_DEVICE_PATH \
+	"auto_install/ai_instance/target/target_device/disk/disk_name[name_type='devpath']/name"
+#define	AIM_TARGET_DEVICE_TYPE "auto_install/ai_instance/target/target_device/disk/disk_prop/dev_type"
+#define	AIM_TARGET_DEVICE_SIZE	\
+	"auto_install/ai_instance/target/target_device/disk/disk_prop/dev_size"
+#define	AIM_TARGET_DEVICE_VENDOR	\
+	"auto_install/ai_instance/target/target_device/disk/disk_prop/dev_vendor"
+#define	AIM_TARGET_DEVICE_USE_SOLARIS_PARTITION	\
+	"auto_install/ai_instance/target/target_device/disk/partition[action='use_existing']/action"
+#define	AIM_TARGET_DEVICE_INSTALL_SLICE_NUMBER \
+	"auto_install/ai_instance/target/target_device/disk/slice[is_root='true']/name"
+#define	AIM_TARGET_DEVICE_ISCSI_TARGET_NAME \
+	"auto_install/ai_instance/target/target_device/disk/iscsi/name"
+#define	AIM_TARGET_DEVICE_ISCSI_TARGET_IP \
+	"auto_install/ai_instance/target/target_device/disk/iscsi/ip"
+#define	AIM_TARGET_DEVICE_ISCSI_TARGET_LUN \
+	"auto_install/ai_instance/target/target_device/disk/iscsi/target_lun"
+#define	AIM_TARGET_DEVICE_ISCSI_TARGET_PORT \
+	"auto_install/ai_instance/target/target_device/disk/iscsi/target_port"
+#define	AIM_TARGET_DEVICE_ISCSI_PARAMETER_SOURCE \
+	"auto_install/ai_instance/target/target_device/disk/iscsi/source"
+#define	AIM_SWAP_SIZE	\
+	"auto_install/ai_instance/target/target_device/swap/zvol/size/val"
+#define	AIM_DUMP_SIZE	\
+	"auto_install/ai_instance/target/target_device/dump/zvol/size/val"
+
+#define	AIM_PARTITION_ACTIONS	\
+	"auto_install/ai_instance/target/target_device/disk/partition/action"
+#define	AIM_NUMBERED_PARTITIONS	\
+	"auto_install/ai_instance/target/target_device/disk/partition/name"
+#define	AIM_NUMBERED_PARTITION_NUMBER	\
+	"auto_install/ai_instance/target/target_device/disk/partition[name=\"%s\":action=\"%s\"]/name"
+#define	AIM_NUMBERED_PARTITION_ACTION	\
+	"auto_install/ai_instance/target/target_device/disk/partition[name=\"%s\":action=\"%s\"]/action"
+#define	AIM_NUMBERED_PARTITION_START_SECTOR	\
+	"auto_install/ai_instance/target/target_device/disk/partition[name=\"%s\":action=\"%s\"]/size/start_sector"
+#define	AIM_NUMBERED_PARTITION_SIZE	\
+	"auto_install/ai_instance/target/target_device/disk/partition[name=\"%s\":action=\"%s\"]/size/val"
+#define	AIM_NUMBERED_PARTITION_TYPE	\
+	"auto_install/ai_instance/target/target_device/disk/partition[name=\"%s\":action=\"%s\"]/part_type"
+
+#define	AIM_USE_EXISTING_PARTITIONS	\
+	"auto_install/ai_instance/target/target_device/disk/partition[action='use_existing']/action"
+#define	AIM_UNNUMBERED_PARTITION_NUMBER	\
+	"auto_install/ai_instance/target/target_device/disk/partition[action='use_existing']/name"
+#define	AIM_UNNUMBERED_PARTITION_ACTION	\
+	"auto_install/ai_instance/target/target_device/disk/partition[action='use_existing']/action"
+#define	AIM_UNNUMBERED_PARTITION_START_SECTOR	\
+	"auto_install/ai_instance/target/target_device/disk/partition[action='use_existing']/size/start_sector"
+#define	AIM_UNNUMBERED_PARTITION_SIZE	\
+	"auto_install/ai_instance/target/target_device/disk/partition[action='use_existing']/size/val"
+#define	AIM_UNNUMBERED_PARTITION_TYPE	\
+	"auto_install/ai_instance/target/target_device/disk/partition[action='use_existing']/part_type"
+
+#define	AIM_SLICE_NUMBER "auto_install/ai_instance/target/target_device/disk/slice/name"
+#define	AIM_SLICE_ACTION "auto_install/ai_instance/target/target_device/disk/slice/action"
+#define	AIM_SLICE_SIZE "auto_install/ai_instance/target/target_device/disk/slice[name=\"%s\":action=\"%s\"]/size/val"
+#define	AIM_SLICE_ON_EXISTING	\
+	"auto_install/ai_instance/target/target_device/disk/slice[name=\"%s\":action=\"%s\"]/force"
+#define	AIM_AUTO_REBOOT	"auto_install/ai_instance/auto_reboot"
+
+#define	AIM_PROXY_URL "auto_install/ai_instance/http_proxy"
+
+#define	AIM_PACKAGE_INSTALL_NAME "auto_install/ai_instance/software/software_data[action='install']/name"
+
+#define	AIM_PACKAGE_REMOVE_NAME "auto_install/ai_instance/software/software_data[action='uninstall']/name"
 
 /*
- * define additional publisher
+ * Primary and secondary publishers
  */
-#define	AIM_IPS_ADDL_PUBLISHER_NAME	\
-	"ai_manifest/ai_pkg_repo_addl_publisher/main/publisher"
-#define	AIM_IPS_ADDL_PUBLISHER_URL	\
-	"ai_manifest/ai_pkg_repo_addl_publisher/main/url"
-#define	AIM_IPS_ADDL_PUBLISHER_MIRROR	\
-	"ai_manifest/ai_pkg_repo_addl_publisher/mirror/url"
-/*
- * define additional authority (backward compatibility)
- */
-#define	AIM_IPS_ADDL_AUTH_NAME	\
-	"ai_manifest/ai_pkg_repo_addl_authority/main/authname"
-#define	AIM_IPS_ADDL_AUTH_URL	\
-	"ai_manifest/ai_pkg_repo_addl_authority/main/url"
-#define	AIM_IPS_ADDL_AUTH_MIRROR	\
-	"ai_manifest/ai_pkg_repo_addl_authority/mirror/url"
+#define	AIM_IPS_PUBLISHER_URL	\
+	"auto_install/ai_instance/software/source/publisher/origin/name"
+#define	AIM_FALLBACK_PUBLISHER_URL	"http://pkg.opensolaris.org/release"
+#define	AIM_FALLBACK_PUBLISHER_NAME	"opensolaris.org"
 
 /*
- * Find default publisher, its mirrors based on url
- */
-#define	AIM_ADD_DEFAULT_URL_PUBLISHER_NAME \
-	"ai_manifest/ai_pkg_repo_default_publisher/main[url=\"%s\"]/publisher"
-#define	AIM_ADD_DEFAULT_URL_PUBLISHER_MIRROR \
-	"ai_manifest/ai_pkg_repo_default_publisher" \
-	"[main/url=\"%s\"]/mirror/url"
-/*
- * Find default authority, its mirrors based on url (backward compatibility)
+ * Find publisher name and mirror based on url
  */
-#define	AIM_ADD_DEFAULT_URL_AUTH_NAME \
-	"ai_manifest/ai_pkg_repo_default_authority/main[url=\"%s\"]/authname"
-#define	AIM_ADD_DEFAULT_URL_AUTH_MIRROR \
-	"ai_manifest/ai_pkg_repo_default_authority" \
-	"[main/url=\"%s\"]/mirror/url"
-/*
- * Find additional publisher, its mirrors based on url
- */
-#define	AIM_ADD_ADDL_URL_PUBLISHER_NAME \
-	"ai_manifest/ai_pkg_repo_addl_publisher/main[url=\"%s\"]/publisher"
-#define	AIM_ADD_ADDL_URL_PUBLISHER_MIRROR \
-	"ai_manifest/ai_pkg_repo_addl_publisher[main/url=\"%s\"]/mirror/url"
-/*
- * Find additional authority, its mirrors based on url (backward compatibility)
- */
-#define	AIM_ADD_ADDL_URL_AUTH_NAME \
-	"ai_manifest/ai_pkg_repo_addl_authority/main[url=\"%s\"]/authname"
-#define	AIM_ADD_ADDL_URL_AUTH_MIRROR \
-	"ai_manifest/ai_pkg_repo_addl_authority[main/url=\"%s\"]/mirror/url"
+#define	AIM_ADD_URL_PUBLISHER_NAME \
+	"auto_install/ai_instance/software/source/publisher[origin/name=\"%s\"]/name"
+#define	AIM_ADD_URL_PUBLISHER_MIRROR \
+	"auto_install/ai_instance/software/source/publisher[origin/name=\"%s\"]/mirror/name"
 
 /* type of package list to be obtained from manifest */
 typedef enum {
@@ -279,9 +229,6 @@
 	char		ip[INSTISCSI_IP_ADDRESS_LEN + 1];
 	uint32_t	port;
 	char		lun[INSTISCSI_MAX_LUN_LEN + 1];
-	char		chapname[INSTISCSI_MAX_CHAP_NAME_LEN + 1];
-	char		chapsecret[INSTISCSI_MAX_CHAP_LEN + 1];
-	char		initiator[INSTISCSI_MAX_INITIATOR_LEN + 1];
 	iscsi_parm_src_t parm_src;
 } iscsi_info_t;
 
@@ -289,6 +236,7 @@
 	/*
 	 * disk criteria for selection of target disk
 	 */
+	char		diskkeyword[10];		/* 'boot_disk' */
 	char		diskname[MAXNAMELEN];
 	char		disktype[MAXNAMELEN];
 	char		diskvendor[MAXNAMELEN];
@@ -299,7 +247,6 @@
 #ifndef	__sparc
 	char		diskusepart[6];		/* 'true' or 'false' */
 #endif
-	char 		diskoverwrite_rpool[6];	/* 'true' or 'false' */
 	iscsi_info_t	diskiscsi;		/* iSCSI target parameters */
 	/*
 	 * other data related to disk target
--- a/usr/src/cmd/auto-install/auto_parse.c	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/auto_parse.c	Fri Aug 20 11:31:18 2010 -0600
@@ -79,9 +79,10 @@
 
 /*
  * Dump errors found during syntactic validation of AI manifest -
- * capture stdout and stderr of xmllint(1M) called with following parameters:
+ * repeat the xmllint(1) call made on the Python side to capture the
+ * stdout and stderr.  xmllint will be called with following parameters:
  *
- * /usr/bin/xmllint --noout --relaxng <schema> <manifest> 2>&1
+ * /usr/bin/xmllint --noout --dtdvalid <schema> --dtdattr <manifest> 2>&1
  *
  * Returns
  * 	-1  - failed to dump syntactic errors
@@ -93,13 +94,12 @@
 	char	*cmd;
 	size_t	cmd_ln;
 	int	ret;
+	char *dtd_xmllint =
+	    "/usr/bin/xmllint --noout --dtdvalid %s --dtdattr %s 2>&1";
 
-	/* calculate size of command string - account for string terminator */
-	cmd_ln = sizeof ("/usr/bin/xmllint --noout --relaxng ") +
-	    strlen(manifest) + sizeof (" ") + strlen(schema) +
-	    sizeof (" 2>&1") + 1;
-
-	cmd = malloc(cmd_ln);
+	/* calculate size of command string */
+	cmd_ln = snprintf(NULL, 0, dtd_xmllint, schema, manifest);
+	cmd = (char *)malloc(cmd_ln + 1);
 
 	if (cmd == NULL) {
 		auto_debug_print(AUTO_DBGLVL_ERR, "malloc() failed\n");
@@ -107,8 +107,7 @@
 		return (-1);
 	}
 
-	(void) snprintf(cmd, cmd_ln,
-	    "/usr/bin/xmllint --noout --relaxng %s %s 2>&1", schema, manifest);
+	(void) snprintf(cmd, cmd_ln + 1, dtd_xmllint, schema, manifest);
 
 	ret = ai_exec_cmd(cmd);
 
@@ -126,6 +125,115 @@
 }
 
 /*
+ * Translate size units from manifest into auto_size_units_t values.
+ *
+ * Defaults to AI_SIZE_UNITS_MEGABYTES if no value given or value
+ * not recognized.
+ *
+ * Returns
+ *  auto_size_units_t
+ */
+static auto_size_units_t
+get_size_units(char *p_str)
+{
+	if ((p_str == NULL) || (! strlen(p_str))) {
+		return (AI_SIZE_UNITS_MEGABYTES);
+	}
+
+	switch (p_str[0]) {
+		case 's':
+		case 'S':
+			return (AI_SIZE_UNITS_SECTORS);
+		case 'g':
+		case 'G':
+			return (AI_SIZE_UNITS_GIGABYTES);
+		case 't':
+		case 'T':
+			return (AI_SIZE_UNITS_TERABYTES);
+		case 'm':
+		case 'M':
+		default:
+			return (AI_SIZE_UNITS_MEGABYTES);
+	}
+}
+
+/*
+ * Convert size from one unit of measurement to another.
+ *
+ * Supported units are the auto_size_units_t enumeration:
+ *  AI_SIZE_UNITS_SECTORS
+ *  AI_SIZE_UNITS_MEGABYTES
+ *  AI_SIZE_UNITS_GIGABYTES
+ *  AI_SIZE_UNITS_TERABYTES
+ * If either from_units or to_units params are not recognized, disk_size is
+ * returned unaltered.
+ *
+ * Returns
+ *  uint64_t
+ */
+static uint64_t
+convert_disk_size(uint64_t disk_size, auto_size_units_t from_units,
+    auto_size_units_t to_units)
+{
+	uint64_t retval = disk_size;
+
+	switch (to_units) {
+		case AI_SIZE_UNITS_SECTORS:
+			switch (from_units) {
+				case AI_SIZE_UNITS_SECTORS:
+					retval = disk_size;
+				case AI_SIZE_UNITS_MEGABYTES:
+					retval = disk_size * MB_TO_SECTORS;
+				case AI_SIZE_UNITS_GIGABYTES:
+					retval = disk_size * GB_TO_MB *
+					    MB_TO_SECTORS;
+				case AI_SIZE_UNITS_TERABYTES:
+					retval = disk_size * TB_TO_GB *
+					    GB_TO_MB * MB_TO_SECTORS;
+			}
+		case AI_SIZE_UNITS_MEGABYTES:
+			switch (from_units) {
+				case AI_SIZE_UNITS_SECTORS:
+					retval = disk_size / MB_TO_SECTORS;
+				case AI_SIZE_UNITS_MEGABYTES:
+					retval = disk_size;
+				case AI_SIZE_UNITS_GIGABYTES:
+					retval = disk_size * GB_TO_MB;
+				case AI_SIZE_UNITS_TERABYTES:
+					retval = disk_size * TB_TO_GB *
+					    GB_TO_MB;
+			}
+		case AI_SIZE_UNITS_GIGABYTES:
+			switch (from_units) {
+				case AI_SIZE_UNITS_SECTORS:
+					retval = disk_size / GB_TO_MB /
+					    MB_TO_SECTORS;
+				case AI_SIZE_UNITS_MEGABYTES:
+					retval = disk_size / GB_TO_MB;
+				case AI_SIZE_UNITS_GIGABYTES:
+					retval = disk_size;
+				case AI_SIZE_UNITS_TERABYTES:
+					retval = disk_size * TB_TO_GB;
+			}
+		case AI_SIZE_UNITS_TERABYTES:
+			switch (from_units) {
+				case AI_SIZE_UNITS_SECTORS:
+					retval = disk_size / MB_TO_SECTORS /
+					    GB_TO_MB / TB_TO_GB;
+				case AI_SIZE_UNITS_MEGABYTES:
+					retval = disk_size / GB_TO_MB /
+					    TB_TO_GB;
+				case AI_SIZE_UNITS_GIGABYTES:
+					retval = disk_size / TB_TO_GB;
+				case AI_SIZE_UNITS_TERABYTES:
+					retval = disk_size;
+			}
+	}
+
+	return (retval);
+}
+
+/*
  * Create the manifest data image in memory.  (Does not validate it.)
  *
  * Import the manifest into an in-memory tree
@@ -226,18 +334,6 @@
 	ai_free_manifest_value_list(value_list);
 }
 
-static char **
-ai_get_manifest_partition_action(int *len)
-{
-	char **value;
-
-	value = ai_get_manifest_values(AIM_PARTITION_ACTION, len);
-
-	if (*len > 0)
-		return (value);
-	return (NULL);
-}
-
 /*
  * ai_get_manifest_element_value() - return value given xml element
  */
@@ -288,6 +384,10 @@
 {
 	char *p;
 
+	p = ai_get_manifest_element_value(AIM_TARGET_DISK_KEYWORD);
+	if (p != NULL)
+		(void) strncpy(adi->diskkeyword, p, sizeof (adi->diskkeyword));
+
 	p = ai_get_manifest_element_value(AIM_TARGET_DEVICE_NAME);
 	if (p != NULL)
 		(void) strncpy(adi->diskname, p, sizeof (adi->diskname));
@@ -314,27 +414,57 @@
 		    sizeof (adi->diskdevicepath));
 
 	p = ai_get_manifest_element_value(AIM_TARGET_DEVICE_SIZE);
-	if (p != NULL)
-		adi->disksize = (uint64_t)strtoull(p, NULL, 0);
+	if (p != NULL) {
+		char *endptr;
+		uint64_t disk_size;
+		auto_size_units_t size_units;
+
+		errno = 0;
+
+		/* Get the numerical portion of the size value */
+		disk_size = (uint64_t)strtoull(p, &endptr, 0);
+
+		if (errno == 0 && endptr != p) {
+			/*
+			 * Get the units portion of the size val and then
+			 * convert the size from given units into number
+			 * of disk sectors.
+			 */
+			size_units = get_size_units(endptr);
+			adi->disksize = convert_disk_size(disk_size, size_units,
+			    AI_SIZE_UNITS_SECTORS);
+
+			auto_debug_print(AUTO_DBGLVL_INFO,
+			    "Requested target size [%s] converted "
+			    "to [%lld] sectors\n",
+			    p, adi->disksize);
+		} else {
+			auto_log_print(
+			    "Invalid target device size specified: [%s]",
+			    p);
+			return (AUTO_INSTALL_FAILURE);
+		}
+	}
 
 	p = ai_get_manifest_element_value(
 	    AIM_TARGET_DEVICE_USE_SOLARIS_PARTITION);
 	if (p != NULL) {
 #ifdef	__sparc
 		auto_log_print("Warning: ignoring manifest element "
-		    "target_device_use_solaris_partition on SPARC\n");
+		    "partition action='use_existing' on SPARC\n");
 #else
-		(void) strncpy(adi->diskusepart, p, sizeof (adi->diskusepart));
+		/*
+		 * In this Schema, a partition with attribute
+		 * action="use_existing" corresponds to
+		 * target_device_use_solaris_partition="true"
+		 * in the previous schema.
+		 */
+		(void) strncpy(adi->diskusepart, "true",
+		    sizeof (adi->diskusepart));
 #endif
 	}
 
 	p = ai_get_manifest_element_value(
-	    AIM_TARGET_DEVICE_OVERWRITE_ROOT_ZFS_POOL);
-	if (p != NULL)
-		(void) strncpy(adi->diskoverwrite_rpool, p,
-		    sizeof (adi->diskoverwrite_rpool));
-
-	p = ai_get_manifest_element_value(
 	    AIM_TARGET_DEVICE_INSTALL_SLICE_NUMBER);
 	if (p != NULL) {
 		int install_slice_number;
@@ -367,24 +497,6 @@
 		adi->diskiscsi.port = strtoll(p, NULL, 0);
 
 	p = ai_get_manifest_element_value(
-	    AIM_TARGET_DEVICE_ISCSI_TARGET_CHAP_NAME);
-	if (p != NULL)
-		(void) strncpy(adi->diskiscsi.chapname, p,
-		    sizeof (adi->diskiscsi.chapname));
-
-	p = ai_get_manifest_element_value(
-	    AIM_TARGET_DEVICE_ISCSI_TARGET_CHAP_SECRET);
-	if (p != NULL)
-		(void) strncpy(adi->diskiscsi.chapsecret, p,
-		    sizeof (adi->diskiscsi.chapsecret));
-
-	p = ai_get_manifest_element_value(
-	    AIM_TARGET_DEVICE_ISCSI_TARGET_INITIATOR);
-	if (p != NULL)
-		(void) strncpy(adi->diskiscsi.initiator, p,
-		    sizeof (adi->diskiscsi.initiator));
-
-	p = ai_get_manifest_element_value(
 	    AIM_TARGET_DEVICE_ISCSI_PARAMETER_SOURCE);
 	if (p == NULL)
 		adi->diskiscsi.parm_src = AI_ISCSI_PARM_SRC_MANIFEST;
@@ -404,6 +516,43 @@
 		}
 	}
 
+	/* Debug - print disk info out to log */
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "Disk info from Manifest:\n");
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskkeyword\t\t\t: [%s]\n", adi->diskkeyword);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskname\t\t\t: [%s]\n", adi->diskname);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdisktype\t\t\t: [%s]\n", adi->disktype);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskvendor\t\t\t: [%s]\n", adi->diskvendor);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskvolname\t\t\t: [%s]\n", adi->diskvolname);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskdevid\t\t\t: [%s]\n", adi->diskdevid);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskdevicepath\t\t: [%s]\n", adi->diskdevicepath);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdisksize\t\t\t: [%d]\n", adi->disksize);
+#ifndef	__sparc
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskusepart\t\t\t: [%s]\n", adi->diskusepart);
+#endif
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.name\t\t: [%s]\n", adi->diskiscsi.name);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.ip\t\t: [%s]\n", adi->diskiscsi.ip);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.port\t\t: [%d]\n", adi->diskiscsi.port);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.lun\t\t: [%s]\n", adi->diskiscsi.lun);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.parm_src\t: [%d] (= %s)\n", adi->diskiscsi.parm_src,
+	    adi->diskiscsi.parm_src ? "DHCP" : "MANIFEST");
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tinstall_slice_num.\t: [%d]\n", adi->install_slice_number);
+
 	return (AUTO_INSTALL_SUCCESS);
 }
 
@@ -422,10 +571,28 @@
 	adsi->swap_size = -1;
 	p = ai_get_manifest_element_value(AIM_SWAP_SIZE);
 	if (p != NULL) {
-		if (sscanf(p, "%ld", &adsi->swap_size) > 0) {
+		char *endptr;
+		int32_t swap_size;
+		auto_size_units_t size_units;
+
+		errno = 0;
+
+		/* Get the numerical portion of the size value */
+		swap_size = (int32_t)strtol(p, &endptr, 0);
+
+		if (errno == 0 && endptr != p) {
+			/*
+			 * Get the units portion of the size val and
+			 * then convert the size from given units into MB.
+			 */
+			size_units = get_size_units(endptr);
+			adsi->swap_size = (int32_t)convert_disk_size(swap_size,
+			    size_units,
+			    AI_SIZE_UNITS_MEGABYTES);
+
 			auto_debug_print(AUTO_DBGLVL_INFO,
-			    "Swap Size Requested=%lu\n",
-			    adsi->swap_size);
+			    "Requested swap size [%s] converted to [%d] MB\n",
+			    p, adsi->swap_size);
 		} else {
 			adsi->swap_size = 0;
 			auto_log_print("Invalid swap size "
@@ -454,10 +621,28 @@
 	addi->dump_size = -1;
 	p = ai_get_manifest_element_value(AIM_DUMP_SIZE);
 	if (p != NULL) {
-		if (sscanf(p, "%ld", &addi->dump_size) > 0) {
+		char *endptr;
+		int32_t dump_size;
+		auto_size_units_t size_units;
+
+		errno = 0;
+
+		/* Get the numerical portion of the size value */
+		dump_size = (int32_t)strtol(p, &endptr, 0);
+
+		if (errno == 0 && endptr != p) {
+			/*
+			 * Get the units portion of the size val and
+			 * then convert the size from given units into MB.
+			 */
+			size_units = get_size_units(endptr);
+			addi->dump_size = (int32_t)convert_disk_size(dump_size,
+			    size_units,
+			    AI_SIZE_UNITS_MEGABYTES);
+
 			auto_debug_print(AUTO_DBGLVL_INFO,
-			    "Dump Size Requested=%lu\n",
-			    addi->dump_size);
+			    "Requested dump size [%s] converted to [%d] MB\n",
+			    p, addi->dump_size);
 		} else {
 			addi->dump_size = 0;
 			auto_log_print("Invalid dump device size "
@@ -472,6 +657,167 @@
 }
 
 /*
+ * Create a partition info struct and populate it with details
+ * from the manifest matching the specified tags (enhanced
+ * nodepaths).
+ *
+ * pstatus - return status pointer, must point to valid storage
+ *		If no problems in validating partition info,
+ *		set to zero, otherwise set to non-zero value
+ *
+ * This function allocates memory for an auto_partition_info
+ * struct. The caller MUST free this memory.
+ */
+static auto_partition_info *
+get_partition_by_tags(char *number_tag, char *action_tag,
+    char *start_tag, char *size_tag, char *type_tag, int *pstatus)
+{
+	auto_partition_info *api;
+	char *p;
+	char *endptr;
+
+	api = calloc(sizeof (auto_partition_info), 1);
+	if (api == NULL)
+		return (NULL);
+
+	/* Get the name (number) for this partition */
+	p = ai_get_manifest_element_value(number_tag);
+	if (p != NULL) {
+		errno = 0;
+		api->partition_number = (int) strtoul(p, &endptr, 10);
+		if (errno != 0 || endptr == p) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Partition name in manifest (%s) is "
+			    "not a valid value.\n",
+			    p);
+			*pstatus = 1;
+			free(api);
+			errno = 0;
+			return (NULL);
+		}
+	}
+
+	/* Get the action for this partition */
+	p = ai_get_manifest_element_value(action_tag);
+	if (p != NULL)
+		(void) strlcpy(api->partition_action, p,
+		    sizeof (api->partition_action));
+
+	/*
+	 * Get the start_sector for this partition
+	 *
+	 * set default for starting sector (unspecified)
+	 * stored as unsigned in C, * but signed in XML
+	 * so that -1 can be used in default value manifest
+	 * to tell AI to find best location when starting sector not specified
+	 * see om_create_partition()
+	 */
+	api->partition_start_sector = (uint64_t)-1LL;
+	p = ai_get_manifest_element_value(start_tag);
+	if (p != NULL) {
+		api->partition_start_sector =
+		    (uint64_t)strtoll(p, NULL, 0);
+	}
+
+	/*
+	 * Get the size (value + units) for this partition.
+	 * This is only used for "create" action.
+	 */
+	if (strcmp(api->partition_action, "create") == 0) {
+		p = ai_get_manifest_element_value(size_tag);
+		if (p != NULL) {
+			errno = 0;
+
+			/* Get the numerical portion of the size value */
+			api->partition_size = strtoull(p, &endptr, 0);
+
+			if (errno == 0 && endptr != p) {
+				/* Get the units portion of the size value */
+				api->partition_size_units =
+				    get_size_units(endptr);
+			} else {
+				auto_debug_print(AUTO_DBGLVL_ERR,
+				    "Partition size in manifest (%s) is "
+				    "not a valid value.\n",
+				    p);
+				*pstatus = 1;
+				free(api);
+				errno = 0;
+				return (NULL);
+			}
+		} else {
+			/*
+			 * Default to 0mb.  This is not strictly necessary,
+			 * as both these values correspond to 0, which was
+			 * the value they were already set to when the struct
+			 * was calloc()ed.
+			 */
+			api->partition_size = (uint64_t) 0;
+			api->partition_size_units = AI_SIZE_UNITS_MEGABYTES;
+		}
+	}
+
+	/* Get the filesystem type for this partition */
+	p = ai_get_manifest_element_value(type_tag);
+	if (p != NULL) {
+		/* allow some common partition type names */
+		if (strcasecmp(p, "SOLARIS") == 0) {
+			api->partition_type = SUNIXOS2;
+			auto_log_print(
+			    "New Solaris2 partition requested\n");
+		} else if (strcasecmp(p, "DOS16") == 0) {
+			api->partition_type = DOSOS16;
+			auto_log_print(
+			    "New 16-bit DOS partition requested\n");
+		} else if (strcasecmp(p, "FAT32") == 0) {
+			api->partition_type = FDISK_WINDOWS;
+			auto_log_print(
+			    "New FAT32 partition requested\n");
+		} else if (strcasecmp(p, "DOSEXT") == 0) {
+			api->partition_type = EXTDOS;
+			auto_log_print(
+			    "New DOS extended partition requested\n");
+		} else if (strcasecmp(p, "DOSEXTLBA") == 0) {
+			api->partition_type = FDISK_EXTLBA;
+			auto_log_print(
+			    "New DOS extended LBA partition requested"
+			    "\n");
+		} else {
+            /*
+             * Use partition type number, eg "191" to
+             * represent a Solaris partition.
+             */
+			char *endptr;
+
+			errno = 0;
+			api->partition_type =
+			    strtoull(p, &endptr, 0);
+			if (errno != 0 || endptr == p) {
+				auto_debug_print(AUTO_DBGLVL_ERR,
+				    "Partition type in manifest (%s) is "
+				    "not a valid number or partition type.\n",
+				    p);
+				*pstatus = 1;
+				free(api);
+				errno = 0;
+				return (NULL);
+			}
+		}
+	}
+
+	/*
+	 * Determine if this is a logical partition
+	 * This is inferred from the partition number.  Numbers of
+	 * 5 or greater imply the partition must be logical.
+	 */
+	if (api->partition_number >= 5) {
+		api->partition_is_logical = B_TRUE;
+	}
+
+	return (api);
+}
+
+/*
  * Retrieve the information about the partitions
  * that need to be configured
  *
@@ -485,189 +831,245 @@
 auto_partition_info *
 ai_get_manifest_partition_info(int *pstatus)
 {
+	auto_partition_info *ret_api;
 	auto_partition_info *api;
-	int i, len;
-	char **p;
+	int i, j;
+	int actions_len = 0;
+	int numbered_len = 0;
+	int unnumbered_len = 0;
+	char **partition_actions;
+	char **numbered_partitions;
+	char **unnumbered_partitions;
+	char *p;
+	char number_tag[MAXPATHLEN];
+	char action_tag[MAXPATHLEN];
+	char start_tag[MAXPATHLEN];
+	char size_tag[MAXPATHLEN];
+	char type_tag[MAXPATHLEN];
 
 	*pstatus = 0;	/* assume no parsing errors */
 
-	p = ai_get_manifest_partition_action(&len);
-	if (p == NULL)
+	/*
+	 * The name (number) is not mandatory for partitions, but if a partition
+	 * does not have a name then it must have the action 'use_existing'.
+	 * There can only be one 'use_existing' partition specified and it may
+	 * or may not be named.
+	 * We will first see if there is an un-named 'use_existing' partition
+	 * in the manifest and if so, fetch its details.  Then we will fetch
+	 * details for all the named partitions, using name+action as a unique
+	 * key.
+	 */
+
+	unnumbered_partitions = ai_get_manifest_values(
+	    AIM_USE_EXISTING_PARTITIONS, &unnumbered_len);
+	numbered_partitions = ai_get_manifest_values(
+	    AIM_NUMBERED_PARTITIONS, &numbered_len);
+
+	if (unnumbered_partitions == NULL) {
+		/* ai_get_manifest_values sets len to -1 if none found */
+		unnumbered_len = 0;
+	} else {
+		if (unnumbered_len > 1) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Only one 'use_existing' partition is permitted, "
+			    "%d were specified.\n", unnumbered_len);
+			*pstatus = 1;
+			return (NULL);
+		}
+
+		p = ai_get_manifest_element_value(
+		    AIM_UNNUMBERED_PARTITION_NUMBER);
+		if (p != NULL) {
+			/*
+			 * There is one 'use_existing' partition but a
+			 * name (number) was specified with it, so it
+			 * will be handled along with numbered
+			 * partitions - no need for specical handling here.
+			 */
+			unnumbered_len = 0;
+		}
+	}
+
+	if (numbered_partitions == NULL) {
+		/* ai_get_manifest_values sets len to -1 if none found */
+		numbered_len = 0;
+	}
+
+	if ((unnumbered_len + numbered_len) == 0)
 		return (NULL);
 
 	/* len+1 -- '1' for the NULL entry */
-	api = calloc(sizeof (auto_partition_info), len + 1);
+	ret_api = calloc(sizeof (auto_partition_info),
+	    numbered_len + unnumbered_len + 1);
+	if (ret_api == NULL)
+		return (NULL);
 
-	for (i = 0; i < len; i++) {
-		if (strlcpy((api + i)->partition_action, p[i],
-		    AUTO_MAX_ACTION_LEN) >= AUTO_MAX_ACTION_LEN) {
-			auto_debug_print(AUTO_DBGLVL_ERR,
-			    "Partition action in manifest is too long (%s)\n",
-			    p[i]);
-			*pstatus = 1;
-			free(api);
+	if (unnumbered_len) {
+		/*
+		 * We have exactly one partition whose action is 'use_existing'
+		 * which does not have a name (number) specified.  We need
+		 * to fetch its details seperately from the numbered partitions.
+		 */
+		(void) snprintf(number_tag, sizeof (number_tag),
+		    AIM_UNNUMBERED_PARTITION_NUMBER);
+		(void) snprintf(action_tag, sizeof (action_tag),
+		    AIM_UNNUMBERED_PARTITION_ACTION);
+		(void) snprintf(start_tag, sizeof (start_tag),
+		    AIM_UNNUMBERED_PARTITION_START_SECTOR);
+		(void) snprintf(size_tag, sizeof (size_tag),
+		    AIM_UNNUMBERED_PARTITION_SIZE);
+		(void) snprintf(type_tag, sizeof (type_tag),
+		    AIM_UNNUMBERED_PARTITION_TYPE);
+
+		api = get_partition_by_tags(number_tag, action_tag,
+		    start_tag, size_tag, type_tag, pstatus);
+
+		if (api == NULL) {
+			free(unnumbered_partitions);
+			free(ret_api);
 			return (NULL);
 		}
-	}
-	free(p);
 
-	p = get_manifest_element_array(AIM_PARTITION_NUMBER);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			(api + i)->partition_number = atoi(p[i]);
-		}
-		free(p);
+		(void) memcpy(ret_api, api, sizeof (auto_partition_info));
+		free(api);
+
+		free(unnumbered_partitions);
 	}
 
-	/*
-	 * set default for starting sector (unspecified)
-	 * stored as unsigned in C, * but signed in XML
-	 * so that -1 can be used in default value manifest
-	 * to tell AI to find best location when starting sector not specified
-	 * see om_create_partition()
-	 */
-	for (i = 0; i < len; i++) /* if not specified, AI finds best location */
-		(api + i)->partition_start_sector = (uint64_t)-1LL;
-	p = get_manifest_element_array(AIM_PARTITION_START_SECTOR);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			(api + i)->partition_start_sector =
-			    (uint64_t)strtoll(p[i], NULL, 0);
+	if (numbered_len) {
+		partition_actions = ai_get_manifest_values(
+		    AIM_PARTITION_ACTIONS, &actions_len);
+
+		if (partition_actions == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Error fetching partition actions.\n");
+			*pstatus = 1;
+			free(numbered_partitions);
+			free(ret_api);
+			return (NULL);
 		}
-		free(p);
-	}
 
-	p = get_manifest_element_array(AIM_PARTITION_SIZE);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			/* if action is create, size is mandatory */
-			if (strcmp((api + i)->partition_action, "create") != 0)
-				continue;
-			if (p[i] == NULL)	{ /* if size not provided */
-				/* size required for create action */
+		if (unnumbered_len) {
+			/*
+			 * Remove the unnamed 'use_existing' partition from
+			 * partition_actions.
+			 */
+			for (i = 0; i < actions_len; i++) {
+				if (strcmp(partition_actions[i],
+				    "use_existing") == 0) {
+					/*
+					 * Shuffle the remaining items up
+					 * one position.
+					 */
+					for (j = i; j < (actions_len - 1);
+					    j++) {
+						partition_actions[j] =
+						    partition_actions[j+1];
+					}
+					partition_actions[actions_len] = NULL;
+					actions_len--;
+				}
+			}
+		}
+
+		if (numbered_len != actions_len) {
+			if (numbered_len < actions_len) {
+				/*
+				 * If this mismatch occurs, there must have
+				 * been an unnamed partion whose action is
+				 * not 'use_existing'.
+				 */
 				auto_debug_print(AUTO_DBGLVL_ERR,
-				    "Partition size for create action "
-				    "is missing from manifest.\n");
+				    "Invalid unnamed partition specified in "
+				    "manifest. Only one unnamed partition "
+				    "allowed, whose action must be "
+				    "'use_existing'.\n");
 				*pstatus = 1;
-				free(api);
+				free(numbered_partitions);
+				free(partition_actions);
+				free(ret_api);
 				return (NULL);
-			}
-			if (strcasecmp(p[i], "max_size") == 0) {
-				(api + i)->partition_size = OM_MAX_SIZE;
-				/* zero will indicate maximum size */
-				auto_log_print("Maximum size requested for "
-				    "new partition.  (%d)\n", i);
 			} else {
-				char *endptr;
-
-				errno = 0;
-				(api + i)->partition_size =
-				    strtoull(p[i], &endptr, 0);
-				if (errno == 0 && endptr != p[i])
-					continue;
 				auto_debug_print(AUTO_DBGLVL_ERR,
-				    "Partition size in manifest (%s) is "
-				    "not a valid number or \"max_size\".\n",
-				    p[i]);
+				    "Error matching partition actions to "
+				    "names.\n");
 				*pstatus = 1;
-				free(api);
-				errno = 0;
+				free(numbered_partitions);
+				free(partition_actions);
+				free(ret_api);
 				return (NULL);
 			}
 		}
-		free(p);
-	}
 
-	p = get_manifest_element_array(AIM_PARTITION_TYPE);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			/* allow some common partition type names */
-			if (strcasecmp(p[i], "SOLARIS") == 0) {
-				(api + i)->partition_type = SUNIXOS2;
-				auto_log_print(
-				    "New Solaris2 partition requested\n");
-			} else if (strcasecmp(p[i], "DOS16") == 0) {
-				(api + i)->partition_type = DOSOS16;
-				auto_log_print(
-				    "New 16-bit DOS partition requested\n");
-			} else if (strcasecmp(p[i], "FAT32") == 0) {
-				(api + i)->partition_type = FDISK_WINDOWS;
-				auto_log_print(
-				    "New FAT32 partition requested\n");
-			} else if (strcasecmp(p[i], "DOSEXT") == 0) {
-				(api + i)->partition_type = EXTDOS;
-				auto_log_print(
-				    "New DOS extended partition requested\n");
-			} else if (strcasecmp(p[i], "DOSEXTLBA") == 0) {
-				(api + i)->partition_type = FDISK_EXTLBA;
-				auto_log_print(
-				    "New DOS extended LBA partition requested"
-				    "\n");
-			} else {	/* use partition type number */
-				char *endptr;
+		/*
+		 * One or more numbered partitions have been specified.
+		 * Fetch the necessary details for each.
+		 */
+		for (i = 0; i < numbered_len; i++) {
+			(void) snprintf(number_tag, sizeof (number_tag),
+			    AIM_NUMBERED_PARTITION_NUMBER,
+			    numbered_partitions[i], partition_actions[i]);
+			(void) snprintf(action_tag, sizeof (action_tag),
+			    AIM_NUMBERED_PARTITION_ACTION,
+			    numbered_partitions[i], partition_actions[i]);
+			(void) snprintf(start_tag, sizeof (start_tag),
+			    AIM_NUMBERED_PARTITION_START_SECTOR,
+			    numbered_partitions[i], partition_actions[i]);
+			(void) snprintf(size_tag, sizeof (size_tag),
+			    AIM_NUMBERED_PARTITION_SIZE,
+			    numbered_partitions[i], partition_actions[i]);
+			(void) snprintf(type_tag, sizeof (type_tag),
+			    AIM_NUMBERED_PARTITION_TYPE,
+			    numbered_partitions[i], partition_actions[i]);
 
-				errno = 0;
-				(api + i)->partition_type =
-				    strtoull(p[i], &endptr, 0);
-				if (errno == 0 && endptr != p[i])
-					continue;
-				auto_debug_print(AUTO_DBGLVL_ERR,
-				    "Partition type in manifest (%s) is "
-				    "not a valid number or partition type.\n",
-				    p[i]);
-				*pstatus = 1;
-				free(api);
-				errno = 0;
+			api = get_partition_by_tags(number_tag, action_tag,
+			    start_tag, size_tag, type_tag, pstatus);
+
+			if (api == NULL) {
+				free(numbered_partitions);
+				free(ret_api);
 				return (NULL);
 			}
+
+			(void) memcpy((ret_api + unnumbered_len + i),
+			    api, sizeof (auto_partition_info));
+			free(api);
 		}
-		free(p);
+
+		free(numbered_partitions);
 	}
 
-	p = get_manifest_element_array(AIM_PARTITION_SIZE_UNITS);
-	/* partition size units can be sectors, GB, TB, or MB (default) */
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			if (p[i] == NULL) { /* default to MB */
-				(api + i)->partition_size_units =
-				    AI_SIZE_UNITS_MEGABYTES;
-				continue;
-			}
-			switch (p[i][0]) {
-				case 's':
-				case 'S':
-					(api + i)->partition_size_units =
-					    AI_SIZE_UNITS_SECTORS;
-					break;
-				case 'g':
-				case 'G':
-					(api + i)->partition_size_units =
-					    AI_SIZE_UNITS_GIGABYTES;
-					break;
-				case 't':
-				case 'T':
-					(api + i)->partition_size_units =
-					    AI_SIZE_UNITS_TERABYTES;
-					break;
-				case 'm':
-				case 'M':
-				default:
-					(api + i)->partition_size_units =
-					    AI_SIZE_UNITS_MEGABYTES;
-					break;
-			}
-		}
-		free(p);
+	/* Debug - print partition info out to log */
+	api = ret_api;
+	for (; api->partition_action[0] != '\0'; api++) {
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "Partition details from Manifest:\n");
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_action\t\t: [%s]\n",
+		    api->partition_action);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_number\t\t: [%d]\n",
+		    api->partition_number);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_start_sector\t: [%lld]\n",
+		    api->partition_start_sector);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_size\t\t\t: [%lld]\n",
+		    api->partition_size);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_type\t\t\t: [%d]\n",
+		    api->partition_type);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_size_units\t: [%d] (= %s)\n",
+		    (int)api->partition_size_units,
+		    CONVERT_UNITS_TO_TEXT(api->partition_size_units));
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_is_logical\t: [%d] (= %s)\n",
+		    (int)api->partition_is_logical,
+		    api->partition_is_logical ? "true" : "false");
 	}
-	/*
-	 * mark any partitions marked as logical
-	 */
-	p = get_manifest_element_array(AIM_PARTITION_IS_LOGICAL);
-	if (p != NULL)
-		for (i = 0; i < len; i++)
-			if (strcasecmp(p[i], "true") == 0)
-				(api + i)->partition_is_logical = B_TRUE;
-	return (api);
+
+	return (ret_api);
 }
 
 /*
@@ -684,133 +1086,154 @@
 ai_get_manifest_slice_info(int *pstatus)
 {
 	auto_slice_info *asi;
-	int i, len = 0;
-	char **p;
+	auto_slice_info *tmp_asi;
+	int i, names_len = 0, actions_len = 0;
+	char *p;
+	char **slice_names;
+	char **slice_actions;
+	char tag[MAXPATHLEN];
+	char *endptr;
 
 	*pstatus = 0;	/* assume no parsing errors */
-	p = ai_get_manifest_values(AIM_SLICE_ACTION, &len);
-	if (p == NULL || len <= 0)
+
+	/*
+	 * The name (number) and action attributes are mandatory for slices, so
+	 * we will use these two values as the unique key for slice elements.
+	 * First we fetch all the slice numbers and actions, then we query the
+	 * manifest using these values to fetch the additional details for
+	 * each slice.
+	 */
+
+	slice_names = ai_get_manifest_values(AIM_SLICE_NUMBER, &names_len);
+
+	if (slice_names == NULL || names_len <= 0)
 		return (NULL);
 
+	slice_actions = ai_get_manifest_values(AIM_SLICE_ACTION, &actions_len);
+
+	if (actions_len != names_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "Error matching slice names to actions.\n");
+		*pstatus = 1;
+		free(slice_names);
+		free(slice_actions);
+		return (NULL);
+	}
+
 	/* len+1 -- '1' for end of array marker */
-	asi = calloc(sizeof (auto_slice_info), len + 1);
+	asi = calloc(sizeof (auto_slice_info), names_len + 1);
 
-	for (i = 0; i < len; i++) {
-		if (strlcpy((asi + i)->slice_action, p[i],
-		    AUTO_MAX_ACTION_LEN) >= AUTO_MAX_ACTION_LEN) {
-			auto_debug_print(AUTO_DBGLVL_ERR,
-			    "Slice action in manifest is too long (%s)\n",
-			    p[i]);
-			*pstatus = 1;
-			free(asi);
-			return (NULL);
-		}
-	}
-	free(p);
-
-	p = get_manifest_element_array(AIM_SLICE_NUMBER);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			(asi + i)->slice_number = atoi(p[i]);
-		}
-		free(p);
+	if (asi == NULL) {
+		free(slice_names);
+		free(slice_actions);
+		return (NULL);
 	}
 
-	p = get_manifest_element_array(AIM_SLICE_SIZE);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			/* if action is create, size is mandatory */
-			if (p[i] == NULL)	/* if size not provided */
-				/* size required for create action */
-				if (strcmp((asi + i)->slice_action, "create")
-				    != 0)
-					continue;
-				else {
-					auto_debug_print(AUTO_DBGLVL_ERR,
-					    "Slice size for create action "
-					    "is missing from manifest.\n");
-					*pstatus = 1;
-					free(asi);
-					return (NULL);
-				}
-			if (strcasecmp(p[i], "max_size") == 0) {
-				(asi + i)->slice_size = OM_MAX_SIZE;
-				/* zero will indicate maximum size */
-				auto_log_print("Maximum size requested for "
-				    "new slice.  (%d)\n", i);
+	for (i = 0; i < names_len; i++) {
+		/* Get the number for this slice */
+		(asi + i)->slice_number = atoi(slice_names[i]);
+
+		/* Get the action for this slice */
+		if (strlcpy((asi + i)->slice_action, slice_actions[i],
+		    AUTO_MAX_ACTION_LEN) >= AUTO_MAX_ACTION_LEN) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Slice action in manifest is too long (%s)\n", p);
+			*pstatus = 1;
+			free(asi);
+			free(slice_names);
+			free(slice_actions);
+			return (NULL);
+		}
+
+		/* Get the size (value + units) for this slice */
+		(void) snprintf(tag, sizeof (tag), AIM_SLICE_SIZE,
+		    slice_names[i], slice_actions[i]);
+		p = ai_get_manifest_element_value(tag);
+		if (p == NULL) {
+			/*
+			 * Default to 0mb.  This is not strictly necessary,
+			 * as both these values correspond to 0, which was
+			 * the value they were already set to when the struct
+			 * was calloc()ed.
+			 */
+			(asi + i)->slice_size = (uint64_t) 0;
+			(asi + i)->slice_size_units = AI_SIZE_UNITS_MEGABYTES;
+		} else {
+			errno = 0;
+
+			/* Get the numerical portion of the size value */
+			(asi + i)->slice_size = strtoull(p, &endptr, 0);
+
+			if (errno == 0 && endptr != p) {
+				/* Get the units portion of the size value */
+				(asi + i)->slice_size_units =
+				    get_size_units(endptr);
 			} else {
-				char *endptr;
-
-				errno = 0;
-				(asi + i)->slice_size =
-				    strtoull(p[i], &endptr, 0);
-				if (errno == 0 && endptr != p[i])
-					continue;
 				auto_debug_print(AUTO_DBGLVL_ERR,
 				    "Slice size in manifest (%s) is "
-				    "not a valid number or \"max_size\".\n",
-				    p[i]);
+				    "not a valid number.\n",
+				    p);
 				*pstatus = 1;
 				free(asi);
+				free(slice_names);
+				free(slice_actions);
 				errno = 0;
 				return (NULL);
 			}
 		}
-		free(p);
+
+		/*
+		 * Determine behavior for create action on existing slices.
+		 */
+		(void) snprintf(tag, sizeof (tag),
+		    AIM_SLICE_ON_EXISTING, slice_names[i], slice_actions[i]);
+		p = ai_get_manifest_element_value(tag);
+		if (p != NULL) {
+			/*
+			 * Since the slice information array is initialized
+			 * to zero, and the default enum value is also zero,
+			 * the "error" case will also be the default in the
+			 * slice information array.
+			 *
+			 * In the new schema, the slice attribute 'force'
+			 * controls this.  If force="false" (the default)
+			 * then we leave on_existing=0, which equates to
+			 * OM_ON_EXISTING_ERROR. If force="true", then we
+			 * set it to OM_ON_EXISTING_OVERWRITE.
+			 */
+			if (strcasecmp(p, "true") == 0)
+				(asi + i)->on_existing =
+				    OM_ON_EXISTING_OVERWRITE;
+		}
 	}
 
-	p = get_manifest_element_array(AIM_SLICE_SIZE_UNITS);
-	/* slice size units can be sectors, GB, TB, or MB (default) */
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			if (p[i] == NULL) { /* default to MB */
-				(asi + i)->slice_size_units =
-				    AI_SIZE_UNITS_MEGABYTES;
-				continue;
-			}
-			switch (p[i][0]) {
-				case 's':
-				case 'S':
-					(asi + i)->slice_size_units =
-					    AI_SIZE_UNITS_SECTORS;
-					break;
-				case 'g':
-				case 'G':
-					(asi + i)->slice_size_units =
-					    AI_SIZE_UNITS_GIGABYTES;
-					break;
-				case 't':
-				case 'T':
-					(asi + i)->slice_size_units =
-					    AI_SIZE_UNITS_TERABYTES;
-					break;
-				case 'm':
-				case 'M':
-				default:
-					(asi + i)->slice_size_units =
-					    AI_SIZE_UNITS_MEGABYTES;
-					break;
-			}
-		}
-		free(p);
+	free(slice_names);
+	free(slice_actions);
+
+	/* Debug - print slice info out to log */
+	tmp_asi = asi;
+	for (; tmp_asi->slice_action[0] != '\0'; tmp_asi++) {
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "Slice details from Manifest:\n");
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tslice_action\t\t: [%s]\n",
+		    tmp_asi->slice_action);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tslice_number\t\t: [%d]\n",
+		    tmp_asi->slice_number);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tslice_size\t\t\t: [%lld]\n",
+		    tmp_asi->slice_size);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tslice_size_units\t: [%d] (= %s)\n",
+		    (int)tmp_asi->slice_size_units,
+		    CONVERT_UNITS_TO_TEXT(tmp_asi->slice_size_units));
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\ton_existing\t: [%d] (= %s)\n",
+		    (int)tmp_asi->on_existing,
+		    tmp_asi->on_existing ? "OVERWRITE" : "ERROR");
 	}
 
-	/*
-	 * Determine behavior for create action on existing slices.
-	 */
-	p = get_manifest_element_array(AIM_SLICE_ON_EXISTING);
-	if (p != NULL) {
-		/*
-		 * Since the slice information array is initialized to zero,
-		 * and the default enum value is also zero, the "error" case
-		 * will also be the default in the slice information array.
-		 */
-		for (i = 0; i < len; i++)
-			if (p[i] != NULL && strcasecmp(p[i], "overwrite") == 0)
-				(asi + i)->on_existing =
-				    OM_ON_EXISTING_OVERWRITE;
-		free(p);
-	}
 	return (asi);
 }
 
@@ -823,15 +1246,7 @@
 	char	**value;
 	char	*url;
 
-	value = ai_get_manifest_values(AIM_IPS_DEFAULT_PUBLISHER_URL, len);
-
-	/*
-	 * If publisher is not supplied, check for authority
-	 */
-	if (*len <= 0) {
-		value = ai_get_manifest_values(
-		    AIM_IPS_DEFAULT_AUTH_URL, len);
-	}
+	value = ai_get_manifest_values(AIM_IPS_PUBLISHER_URL, len);
 
 	if (*len > 0) {
 		url = value[0];
@@ -842,22 +1257,20 @@
 }
 
 /*
- * Retrieve the URL for the additional publisher
+ * Retrieve the URL(s) for the additional publisher(s)
+ *
+ * Default and additional (or primary and secondary) publishers
+ * now use the same nodepaths, so this function repeats the same
+ * search as ai_get_manifest_default_url() but the results are
+ * handled differently.
  */
 char **
 ai_get_manifest_addl_url(int *len)
 {
 	char	**value;
 
-	value = ai_get_manifest_values(AIM_IPS_ADDL_PUBLISHER_URL, len);
+	value = ai_get_manifest_values(AIM_IPS_PUBLISHER_URL, len);
 
-	/*
-	 * If publisher is not supplied, check for authority
-	 */
-	if (*len <= 0) {
-		value = ai_get_manifest_values(
-		    AIM_IPS_ADDL_AUTH_URL, len);
-	}
 	if (*len > 0) {
 		return (value);
 	}
@@ -866,41 +1279,19 @@
 
 /*
  * Retrieve an publisher name from the manifest using url value
- * This is the common function for default publisher and
- * additional publisher. If the value of the flag is_default_publisher
- * is true, then the default publisher tag is used.
  */
 char *
-ai_get_manifest_repo_publisher(boolean_t is_default_publisher, char *url)
+ai_get_manifest_repo_publisher(char *url)
 {
 	char	**value;
 	char	*publisher;
 	int	len;
 	char	tag[MAXPATHLEN];
 
-	if (is_default_publisher) {
-		(void) snprintf(tag, sizeof (tag),
-		    AIM_ADD_DEFAULT_URL_PUBLISHER_NAME, url);
-	} else {
-		(void) snprintf(tag, sizeof (tag),
-		    AIM_ADD_ADDL_URL_PUBLISHER_NAME, url);
-	}
+	(void) snprintf(tag, sizeof (tag),
+	    AIM_ADD_URL_PUBLISHER_NAME, url);
 	value = ai_get_manifest_values(tag, &len);
 
-	/*
-	 * If publisher is not supplied, check for authority
-	 */
-	if (len <= 0) {
-		if (is_default_publisher) {
-			snprintf(tag, sizeof (tag),
-			    AIM_ADD_DEFAULT_URL_AUTH_NAME, url);
-		} else {
-			snprintf(tag, sizeof (tag),
-			    AIM_ADD_ADDL_URL_AUTH_NAME, url);
-		}
-		value = ai_get_manifest_values(tag, &len);
-	}
-
 	if (len > 0) {
 		publisher = value[0];
 		free(value);
@@ -911,12 +1302,9 @@
 
 /*
  * Retrieve the URL for an IPS repo mirrors
- * This is the common function for default publisher and
- * additional publisher. If the value of the flag is_default_publisher
- * is true, then the default publisher tag is used.
  */
 auto_mirror_repo_t *
-ai_get_manifest_repo_mirrors(boolean_t is_default_publisher, char *url)
+ai_get_manifest_repo_mirrors(char *url)
 {
 	int			i, len = 0;
 	char			**value;
@@ -924,30 +1312,10 @@
 	auto_mirror_repo_t	*ptr, *tmp_ptr;
 	auto_mirror_repo_t	*mirror = NULL;
 
-	if (is_default_publisher) {
-		(void) snprintf(buf, sizeof (buf),
-		    AIM_ADD_DEFAULT_URL_PUBLISHER_MIRROR, url);
-	} else {
-		(void) snprintf(buf, sizeof (buf),
-		    AIM_ADD_ADDL_URL_PUBLISHER_MIRROR, url);
-	}
-
+	(void) snprintf(buf, sizeof (buf),
+	    AIM_ADD_URL_PUBLISHER_MIRROR, url);
 	value = ai_get_manifest_values(buf, &len);
 
-	/*
-	 * If publisher is not supplied, check for authority
-	 */
-	if (len <= 0) {
-		if (is_default_publisher) {
-			(void) snprintf(buf, sizeof (buf),
-			    AIM_ADD_DEFAULT_URL_AUTH_MIRROR, url);
-		} else {
-			(void) snprintf(buf, sizeof (buf),
-			    AIM_ADD_ADDL_URL_AUTH_MIRROR, url);
-		}
-		value = ai_get_manifest_values(buf, &len);
-	}
-
 	if (len <= 0) {
 		return (NULL);
 	}
@@ -996,7 +1364,6 @@
 	char			*current_url, *default_url;
 	int			num_url;
 	auto_repo_info_t 	*repo, *default_repo;
-	boolean_t		is_default_publisher = B_TRUE;
 
 	default_repo = NULL;
 
@@ -1005,7 +1372,11 @@
 	 */
 	current_url = ai_get_manifest_default_url(&num_url);
 	if (current_url == NULL) {
-		return (NULL);
+		/*
+		 * If the publisher wasn't specified in the manifest,
+		 * provide a default value.
+		 */
+		current_url = AIM_FALLBACK_PUBLISHER_URL;
 	}
 
 	repo = calloc(sizeof (auto_repo_info_t), 1);
@@ -1017,9 +1388,18 @@
 	 * Save the value before calling another ai_get_manifest_*()
 	 */
 	default_url = strdup(current_url);
-	p = ai_get_manifest_repo_publisher(is_default_publisher, default_url);
+	p = ai_get_manifest_repo_publisher(default_url);
 	if (p == NULL) {
-		goto get_out;
+		/*
+		 * If the primary publisher URL is AIM_FALLBACK_PUBLISHER_URL
+		 * and no name was specified, then provide a default value.
+		 * For all other URLs, if a name is not specified for the
+		 * publisher, then it is an error.
+		 */
+		if (strcasecmp(current_url, AIM_FALLBACK_PUBLISHER_URL) == 0)
+			p = AIM_FALLBACK_PUBLISHER_NAME;
+		else
+			goto get_out;
 	}
 	repo->publisher = strdup(p);
 	repo->url = strdup(default_url);
@@ -1031,7 +1411,7 @@
 	 * get the mirrors for this publishers
 	 */
 	repo->mirror_repo =
-	    ai_get_manifest_repo_mirrors(is_default_publisher, default_url);
+	    ai_get_manifest_repo_mirrors(default_url);
 	repo->next_repo = NULL;
 	default_repo = repo;
 
@@ -1063,7 +1443,6 @@
 	char			**urls;
 	int			i,  num_url;
 	auto_repo_info_t 	*repo, *tmp_repo, *addl_repo;
-	boolean_t		is_default_publisher = B_FALSE;
 
 	addl_repo = NULL;
 	tmp_repo = NULL;
@@ -1077,10 +1456,15 @@
 		return (NULL);
 
 	/*
+	 * We start iterating through the urls at index 1
+	 * instead of index 0, because the first url returned
+	 * is the primary publisher.  All subsequent urls are
+	 * secondary, or additional, publishers, which is what
+	 * we want here.
 	 * Allocate space and save the urls because the next
 	 * call to ai_get_manifest_*() will overwrite them
 	 */
-	for (i = 0; i < num_url; i++) {
+	for (i = 1; i < num_url; i++) {
 		/*
 		 * Ignore the empty string
 		 */
@@ -1112,8 +1496,7 @@
 	 * mirrors (if any).
 	 */
 	for (repo = addl_repo; repo != NULL; repo = repo -> next_repo) {
-		p = ai_get_manifest_repo_publisher(
-		    is_default_publisher, repo->url);
+		p = ai_get_manifest_repo_publisher(repo->url);
 		if (p == NULL) {
 			goto get_out;
 		}
@@ -1125,8 +1508,7 @@
 		/*
 		 * get the mirrors for this publisher
 		 */
-		repo->mirror_repo = ai_get_manifest_repo_mirrors(
-		    is_default_publisher, repo->url);
+		repo->mirror_repo = ai_get_manifest_repo_mirrors(repo->url);
 	}
 
 	free(urls);
--- a/usr/src/cmd/auto-install/auto_td.c	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/auto_td.c	Fri Aug 20 11:31:18 2010 -0600
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <assert.h>
@@ -39,7 +38,6 @@
 #include <auto_install.h>
 #include <orchestrator_api.h>
 
-#define	MB_TO_SECTORS	((uint64_t)2048)
 #define	NULLCHK(ptr, alternate_text) ((ptr) == NULL ? (alternate_text) : (ptr))
 #define	TAG_IS_TRUE(tag) (strncasecmp(tag, "true", sizeof (tag)) == 0)
 #define	DISK_CRIT_SPECIFIED(crit) ((crit)[0] != '\0')
@@ -211,7 +209,7 @@
 
 	/* check if boot disk required as target for the installation */
 
-	if (strcasecmp(adi->diskname, AIM_TARGET_DEVICE_BOOT_DISK) == 0) {
+	if (strcasecmp(adi->diskkeyword, AIM_TARGET_DEVICE_BOOT_DISK) == 0) {
 		if (((di = om_get_boot_disk(disks)) == NULL) ||
 		    (di->disk_name == NULL)) {
 			auto_log_print(gettext("Boot disk specified as "
@@ -595,6 +593,8 @@
 static boolean_t
 disk_criteria_specified(auto_disk_info *adi)
 {
+	if (adi->diskkeyword[0] != '\0')
+		return (B_TRUE);
 	if (adi->diskname[0] != '\0')
 		return (B_TRUE);
 	if (adi->diskvolname[0] != '\0')
@@ -613,8 +613,6 @@
 	if (adi->diskusepart[0] != '\0')
 		return (B_TRUE);
 #endif
-	if (adi->diskoverwrite_rpool[0] != '\0')
-		return (B_TRUE);
 	return (B_FALSE);
 }
 
@@ -918,6 +916,9 @@
 static void
 dump_disk_criteria(auto_disk_info *adi)
 {
+	if (adi->diskkeyword[0] != '\0')
+		auto_log_print(gettext(" Disk keyword:"
+		    " %s\n"), adi->diskkeyword);
 	if (adi->diskname[0] != '\0')
 		auto_log_print(gettext(" Disk name: %s\n"), adi->diskname);
 	if (adi->diskvolname[0] != '\0')
@@ -939,8 +940,5 @@
 		auto_log_print(gettext(" Use existing Solaris partition:"
 		    " %s\n"), adi->diskusepart);
 #endif
-	if (adi->diskoverwrite_rpool[0] != '\0')
-		auto_log_print(gettext(" Use existing ZFS root pool 'rpool':"
-		    " %s\n"), adi->diskoverwrite_rpool);
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/configuration.dtd	Fri Aug 20 11:31:18 2010 -0600
@@ -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|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">
+
--- a/usr/src/cmd/auto-install/default.xml	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/default.xml	Fri Aug 20 11:31:18 2010 -0600
@@ -1,129 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
-CDDL HEADER START
+ 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.
+ 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.
+ 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]
+ 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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
--->
+ CDDL HEADER END
 
-<ai_criteria_manifest>
-    <ai_embedded_manifest>
-        <ai_manifest name="default">
-	    <!--
-                Add missing driver packages to a booted install image so an
-                installation can complete.  Add packages to target as well.
-                <searchall> searches and installs from configured repo.
-            -->
-	    <!--
-		Uncomment before using
-            <ai_add_drivers>
-                <searchall/>
-            </ai_add_drivers>
-	    -->
-            <ai_pkg_repo_default_publisher>
-                <main url="http://pkg.opensolaris.org/release" publisher="opensolaris.org"/>
-                <mirror url=""/>
-            </ai_pkg_repo_default_publisher>
+ Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 
-            <!--
-                By default the latest build available, in the specified IPS
-                repository, is installed.
-                If another build is required, the build number has
-                to be appended to the 'entire' package in following
-                form:
+-->
+<!DOCTYPE auto_install SYSTEM "file:///usr/share/auto_install/ai.dtd">
+<auto_install>
+  <ai_instance name="default">
+    <software>
+      <source>
+        <publisher name="opensolaris.org">
+          <origin name="http://pkg.opensolaris.org/release"/>
+        </publisher>
+      </source>
+
+     <!--
+	By default the latest build available, in the specified IPS
+	repository, is installed.  If another build is required, the 
+	build number has to be appended to the 'entire' package in following
+	form:
+
+	<name>pkg:/[email protected]#</name>
+      --> 
 
-                <pkg name="pkg:/[email protected]#"/>
-            -->
-            <ai_install_packages>
-		<pkg name="pkg:/entire"/>
-		<pkg name="pkg:/babel_install"/>
-		<!--
-		    The following two packages are required by iSCSI,
-		    and can be deleted from this list if iSCSI is not used
-		    See iscsiadm(1m) man page for more information
-		-->
-		<pkg name="pkg:/network/iscsi/initiator"/>
-		<pkg name="pkg:/network/iscsi/iser"/>
-            </ai_install_packages>
-	    <!--
-	        babel_install and slim_install are group packages used to
-	        define the default installation.  They are removed here so
-	        that they do not inhibit removal of other packages on the
-	        installed system.
-	    --> 
-           <ai_uninstall_packages>
-		    <pkg name="pkg:/babel_install"/>
-		    <pkg name="pkg:/slim_install"/>
-            </ai_uninstall_packages>
-            <ai_auto_reboot>
-                false
-            </ai_auto_reboot>
-        </ai_manifest>
-    </ai_embedded_manifest>
-    <sc_embedded_manifest name = "AI">
-        <!-- <?xml version='1.0'?>
-        <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
-        <service_bundle type="profile" name="system configuration">
-            <service name="system/install/config" version="1" type="service">
-                <instance name="default" enabled="true">
-                    <property_group name="user_account" type="application">
-                        <propval name="login" type="astring" value="jack"/>
-                        <propval name="password" type="astring" value="9Nd/cwBcNWFZg"/>
-                        <propval name="description" type="astring" value="default_user"/>
-                        <propval name="shell" type="astring" value="/usr/bin/bash"/>
-                        <propval name="uid" type='count' value='101'/>
-                        <propval name="gid" type='count' value='10'/>
-                        <propval name="type" type="astring" value="normal"/>
-                        <propval name="roles" type="astring" value="root"/>
-                    </property_group>
+      <software_data action="install" type="IPS">
+        <name>pkg:/entire</name>
+        <name>pkg:/babel_install</name>
+      </software_data>
+      <!--
+	    babel_install and slim_install are group packages used to
+	    define the default installation.  They are removed here so
+	    that they do not inhibit removal of other packages on the
+	    installed system.
+        -->
+      <software_data action="uninstall" type="IPS">
+        <name>pkg:/babel_install</name>
+        <name>pkg:/slim_install</name>
+      </software_data>
+    </software>
+   <!--
+	Add missing driver packages to a booted install image so an
+	installation can complete.  Add packages to target as well.
+	<search_all> searches and installs from configured repo.
+    -->
+	<!--
+                Uncomment before using
+            <add_drivers>
+                <search_all/>
+            </add_drivers>
+	--> 
+    <sc_embedded_manifest name="AI">
+      <!-- <?xml version='1.0'?>
+      <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+      <service_bundle type="profile" name="system configuration">
+        <service name="system/install/config" version="1" type="service">
+          <instance name="default" enabled="true">
+            <property_group name="user_account" type="application">
+              <propval name="login" type="astring" value="jack"/>
+              <propval name="password" type="astring" value="9Nd/cwBcNWFZg"/>
+              <propval name="description" type="astring" value="default_user"/>
+              <propval name="shell" type="astring" value="/usr/bin/bash"/>
+              <propval name="uid" type='count' value='101'/>
+              <propval name="gid" type='count' value='10'/>
+              <propval name="type" type="astring" value="normal"/>
+              <propval name="roles" type="astring" value="root"/>
+            </property_group>
 
-                    <property_group name="root_account" type="application">
-                        <propval name="password" type="astring" value="$5$VgppCOxA$ycFmYW4ObRRHhtsGEygDdexk5bugqgSiaSR9niNCouC"/>
-                        <propval name="type" type="astring" value="role"/>
-                    </property_group>
+            <property_group name="root_account" type="application">
+              <propval name="password" type="astring" value="$5$VgppCOxA$ycFmYW4ObRRHhtsGEygDdexk5bugqgSiaSR9niNCouC"/>
+              <propval name="type" type="astring" value="role"/>
+            </property_group>
 
-                    <property_group name="other_sc_params" type="application">
-                        <propval name="timezone" type="astring" value="GMT"/>
-                        <propval name="hostname" type="astring" value="opensolaris"/>
-                    </property_group>
-                </instance>
-            </service>
-
-            <service name="system/console-login" version="1" type="service">
-                <property_group name="ttymon" type="application">
-                    <propval name="terminal_type" type="astring" value="sun"/>
-                </property_group>
-            </service>
+            <property_group name="other_sc_params" type="application">
+              <propval name="timezone" type="astring" value="GMT"/>
+              <propval name="hostname" type="astring" value="opensolaris"/>
+            </property_group>
+          </instance>
+        </service>
+        <service name="system/console-login" version="1" type="service">
+          <property_group name="ttymon" type="application">
+            <propval name="terminal_type" type="astring" value="sun"/>
+          </property_group>
+        </service>
 
-            <service name='system/keymap' version='1' type='service'>
-                <instance name='default' enabled='true'>
-                    <property_group name='keymap' type='system'>
-                        <propval name='layout' type='astring' value='US-English'/>
-                    </property_group>
-                </instance>
-            </service>
+        <service name='system/keymap' version='1' type='service'>
+          <instance name='default' enabled='true'>
+            <property_group name='keymap' type='system'>
+              <propval name='layout' type='astring' value='US-English'/>
+            </property_group>
+          </instance>
+        </service>
 
-            <service name="network/physical" version="1" type="service">
-                <instance name="nwam" enabled="true"/>
-                <instance name="default" enabled="false"/>
-            </service>
-        </service_bundle>
-    -->
+        <service name="network/physical" version="1" type="service">
+          <instance name="nwam" enabled="true"/>
+          <instance name="default" enabled="false"/>
+        </service>
+      </service_bundle>
+      -->
     </sc_embedded_manifest>
-</ai_criteria_manifest>
-
+  </ai_instance>
+</auto_install>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/software.dtd	Fri Aug 20 11:31:18 2010 -0600
@@ -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.
+
+-->
+
+<!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>
+-->
+<!ELEMENT property (#PCDATA)>
+<!ATTLIST property val (true|false) #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>
--- a/usr/src/cmd/auto-install/svc/auto-installer	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/svc/auto-installer	Fri Aug 20 11:31:18 2010 -0600
@@ -20,8 +20,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 # Use tee(1) when posting messages to both console and log file
@@ -44,6 +43,7 @@
 ISA_INFO=`/usr/bin/uname -p`
 PRTCONF=/usr/sbin/prtconf
 SVCPROP=/usr/bin/svcprop
+SED=/usr/bin/sed
 
 . /lib/svc/share/smf_include.sh
 
@@ -97,18 +97,13 @@
 # is stored. This is just temporary solution, better approach should be to
 # point XML validator to where these file are.
 #
-if [ ! -f /usr/share/auto_install/ai_manifest.rng ] ; then
-        echo "Couldn't find /usr/share/auto_install/ai_manifest.rng"
-        exit $SMF_EXIT_ERR_FATAL
-fi
-/usr/bin/cp /usr/share/auto_install/ai_manifest.rng /tmp/
-
-if [ ! -f /usr/share/auto_install/ai_manifest.defval.xml ] ; then
-        echo "Couldn't find /usr/share/auto_install/ai_manifest.defval.xml"
-        exit $SMF_EXIT_ERR_FATAL
-fi
-/usr/bin/cp /usr/share/auto_install/ai_manifest.defval.xml /tmp/
-
+for dtd_file in "ai.dtd" "configuration.dtd" "software.dtd" "target.dtd" ; do
+        if [ ! -f "/usr/share/auto_install/$dtd_file" ] ; then
+             echo "Couldn't find /usr/share/auto_install/$dtd_file"
+             exit $SMF_EXIT_ERR_FATAL
+        fi
+        /usr/bin/cp "/usr/share/auto_install/$dtd_file" /tmp/
+done
 
 echo "" | $TEE_LOGTOCONSOLE
 echo "Automated Installation started" | $TEE_LOGTOCONSOLE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/target.dtd	Fri Aug 20 11:31:18 2010 -0600
@@ -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/cmd/auto-install/xslt/README	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,10 @@
+This directory contains an XSLT file for transforming old-schema AI Manifests
+into new-schema equivalents and a Python wrapper script for executing the
+transformation via the xsltproc program.
+
+These files are expected to be of use to QE teams and users who have a number
+of custom AI Manifests that adhere to the AI Manifest schema in effect prior
+to build 147 and who wish to transform these manifests for use by AI in
+builds 147 and later.
+
+These files are not currently delivered as part of any package.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/xslt/old-to-new.py	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,294 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+import sys
+import os
+import getopt
+from subprocess import Popen, PIPE
+
+
+XSLT_PROC="/usr/bin/xsltproc"
+XSLT_FILE="old-to-new.xslt"
+
+
+def usage(exitcode=1):
+    '''
+        Print help page and exit script.
+
+        Default exit code is 1, which indicates an error.  To exit
+        normally, set keyword param exitcode to 0.
+    '''
+    this_script = os.path.basename(sys.argv[0])
+
+    print ""
+    print "%s - Convert old-style XML AI Manifests to the new schema." % this_script
+    print ""
+    print "For convenience, the command-line interface has similar semantics to cp(1)."
+    print ""
+    print "Usage:"
+    print "\t%s [options] infile outfile" % this_script
+    print "\t%s [options] infile... outdir" % this_script
+    print "\t%s -r [options] indir... outdir" % this_script
+    print "\nOptions:"
+    print "\t-f        : force overwrite if output already exists"
+    print "\t-r        : recursively transform .xml files in named sub-directory"
+    print "\t-h --help : print this help page and exit"
+
+    sys.exit(exitcode)
+
+
+def run_cmd(cmd):
+    '''
+        Execute the given command in a subprocess.
+
+        On success, returns the stdout from running the command.
+        On failure, raises one of:
+            OSError
+            ValueError
+            Exception
+    '''
+
+    try:
+        cmd_popen = Popen(cmd, shell=True, stdout=PIPE)
+        (cmd_stdout, cmd_stderr) = cmd_popen.communicate()
+    except OSError, err:
+        print "ERROR running [%s] : [%s]" % \
+            (cmd, str(err))
+        raise
+    except ValueError, err:
+        print "ERROR running [%s] : [%s]" % \
+            (cmd, str(err))
+        raise
+
+    if cmd_popen.returncode != 0:
+        errstr = "ERROR: command [%s] returned [%d] : [%s]" % \
+            (cmd, cmd_popen.returncode, str(cmd_stderr))
+        print errstr
+        raise Exception, (errstr)
+
+    if cmd_stderr is not None:
+        print "WARNING: command [%s] produced stderr output: [%s]" % \
+            (cmd, cmd_stderr)
+
+    return cmd_stdout
+
+
+def do_transform(xsltfile, infile, outfile, mkdirs=False, overwrite=False):
+    '''
+        Create the output directory, if appropriate, and run the xsltproc
+        command to transform infile to oufile.
+
+        On success, returns True.
+        On failure, returns False.
+    '''
+
+    # Normalize the paths so we can check if they are really the same file
+    # (doesn't always work, eg for paths beginning with "..")
+    infile = os.path.normpath(infile)
+    outfile = os.path.normpath(outfile)
+
+    if infile == outfile:
+        print "ERROR: source [%s] and target [%s] are the same" % \
+            (infile, outfile)
+        return False
+
+    outdir = os.path.dirname(outfile)
+    if (len(outdir)) and (not os.path.isdir(outdir)):
+        if os.path.exists(outdir):
+            print "ERROR: target dir [%s] is not a directory" % \
+                outdir
+            return False
+
+        if not mkdirs:
+            print "ERROR: target dir [%s] doesn't exist" % \
+                outdir
+            return False
+
+        try:
+            os.makedirs(outdir)
+        except OSError, err:
+            print "ERROR: failed to make dir [%s] : [%s]" % \
+                (outdir, str(err))
+            return False
+
+    if (os.path.exists(outfile)) and (not overwrite):
+        print "ERROR: target file [%s] already exists. Use -f." % \
+            outfile
+        return False
+
+    # Construct command
+    cmd = "%s -o %s %s %s" % (XSLT_PROC, outfile, xsltfile, infile)
+
+    try:
+        output = run_cmd(cmd)
+    except:
+        return False
+
+    return True
+
+
+def do_main():
+    '''
+        Process command line options and call do_transform() for
+        each file to be processed.
+
+        Returns: nothing.
+    '''
+
+    sources = []
+    target = None
+    force_overwrite = False
+    recursive = False
+    target_exists = False
+
+    # Check xsltproc is installed
+    if not os.access(XSLT_PROC, os.X_OK):
+        print "ERROR: Cannot find %s" % XSLT_PROC
+        print "You may be able to install it with:"
+        print "\tpfexec pkg install pkg:/library/libxslt"
+        sys.exit(1)
+
+    # Check xsl transform file is available in same dir this
+    # script was run from
+    xsltdir = os.path.dirname(sys.argv[0])
+    xsltfile = "%s/%s" % (xsltdir, XSLT_FILE)
+    if (not os.path.exists(xsltfile)):
+        print "XSLT file [%s] is missing from directory [%s]" % \
+            (XSLT_FILE, xsltdir)
+        sys.exit(1)
+
+    # Fetch and process command line params and options
+    try:
+        optlist, args = getopt.getopt(sys.argv[1:], "frh", ["help"])
+    except getopt.GetoptError:
+        usage()
+
+    for opt, arg in optlist:
+        if (opt == "-f"):
+            force_overwrite = True
+        if (opt == "-r"):
+            recursive = True
+        if (opt == "-h") or (opt == "--help"):
+            usage(exitcode=0)
+
+    # There must be at least 2 params.  The last param is the
+    # target; all the other params are the source(s).
+    if len(args) < 2:
+        usage()
+
+    sources = args[:len(args) - 1]
+    target = args[len(args) - 1]
+
+    # note whether the target existed before we started
+    if os.path.exists(target):
+        target_exists = True
+
+    # Check for invalid paramaters (pt. 1)
+    if ((len(sources) > 1) and
+        (not os.path.isdir(target))):
+        # if there are multiple sources (files or dirs), then
+        # target must be an existing directory
+        print "ERROR: [%s] is not a directory" % \
+            target
+        sys.exit(1)
+
+    for source in sources:
+        # normalize source path
+        source = os.path.normpath(source)
+
+        # Check for invalid paramaters (pt. 2)
+        if source == "/":
+            print "ERROR: '/' not allowed"
+            sys.exit(1)
+        if not os.path.exists(source):
+            print "ERROR: no such file or directory: [%s]" % \
+                source
+            sys.exit(1)
+        if (os.path.isdir(source)) and (not recursive):
+            print "ERROR: [%s] is a directory, but '-r' not specified" % \
+                source
+            sys.exit(1)
+        if (not os.path.isdir(source)) and (recursive):
+            print "ERROR: [%s] is not a directory, but '-r' was specified" % \
+                source
+            sys.exit(1)
+        if ((os.path.isdir(source)) and
+            (os.path.exists(target)) and
+            (not os.path.isdir(target))):
+            print "ERROR: [%s] is not a directory" % \
+                target
+            sys.exit(1)
+
+        if os.path.isdir(source):
+            # recursively iterate through source dir, processing each file
+            for dirpath, dirnames, filenames in os.walk(source):
+                # alter dirnames in-place to skip .*
+                dirnames[:] = [d for d in dirnames if not d.startswith('.')]
+
+                for name in filenames:
+                    srcfile = os.path.join(dirpath, name)
+
+                    partial_dstfile = os.path.join(dirpath, name)
+
+                    # replicate how cp -r treats sub-dirs:
+                    # 1. if source contains multiple sub-dirs, eg "a/b/c"
+                    # then only create rightmost one, eg "c", under target
+                    index = source.rfind("/", 1)
+                    if index != -1:
+                        # ensure partial_dstfile begins with source
+                        if partial_dstfile.find(source) == 0:
+                            partial_dstfile = partial_dstfile[index+1:]
+
+                    # replicate how cp -r treats sub-dirs:
+                    # 2. if target already existed then chop off leftmost
+                    # dir of source from target
+                    if not target_exists:
+                        index = partial_dstfile.find("/", 1)
+                        if index != -1:
+                            partial_dstfile = partial_dstfile[index+1:]
+
+                    dstfile = os.path.join(target, partial_dstfile)
+
+                    if not do_transform(xsltfile, srcfile, dstfile,
+                        mkdirs=True, overwrite=force_overwrite):
+                        print "ERROR: Transform failed."
+                        sys.exit(1)
+        elif os.path.isdir(target):
+            dstfile = os.path.join(target, os.path.basename(source))
+
+            if not do_transform(xsltfile, source, dstfile,
+                mkdirs=False, overwrite=force_overwrite):
+                print "ERROR: Transform failed."
+                sys.exit(1)
+        else:
+            # this must be a simple "single infile" -> "single outfile" job
+            if not do_transform(xsltfile, source, target,
+                mkdirs=False, overwrite=force_overwrite):
+                print "ERROR: Transform failed."
+                sys.exit(1)
+
+
+if __name__ == "__main__":
+    do_main()
+
+    sys.exit(0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/xslt/old-to-new.xslt	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,702 @@
+<?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:///usr/share/auto_install/ai.dtd"/>
+
+    <xsl:template match="/">
+        <!-- We need to process both criteria manifests (with embedded AI -->
+        <!-- and/or SC manifests) and simple AI manifests. That is, both: -->
+        <!--     <ai_criteria_manifest> -->
+        <!--         <ai_embedded_manifest> -->
+        <!--             <ai_manifest> -->
+        <!--                 ... -->
+        <!--             </ai_manifest> -->
+        <!--         </ai_embedded_manifest> -->
+        <!--         <sc_embedded_manifest> -->
+        <!--             ... -->
+        <!--         </sc_embedded_manifest> -->
+        <!--     </ai_criteria_manifest> -->
+        <!-- and: -->
+        <!--     <ai_manifest> -->
+        <!--         ... -->
+        <!--     </ai_manifest> -->
+
+        <xsl:if test="ai_criteria_manifest or ai_manifest">
+            <auto_install>
+                <xsl:apply-templates select="ai_criteria_manifest"/>
+                <xsl:apply-templates select="ai_manifest"/>
+            </auto_install>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="ai_criteria_manifest">
+        <xsl:apply-templates select="ai_embedded_manifest"/>
+        <xsl:apply-templates select="sc_embedded_manifest"/>
+    </xsl:template>
+
+    <xsl:template match="ai_embedded_manifest">
+        <xsl:apply-templates select="ai_manifest"/>
+    </xsl:template>
+
+
+    <xsl:template match="ai_manifest">
+        <ai_instance name="{@name}">
+            <xsl:if test="ai_auto_reboot">
+                <xsl:attribute name="auto_reboot">
+                    <xsl:value-of select="normalize-space(ai_auto_reboot)"/>
+                </xsl:attribute>
+            </xsl:if>
+
+            <xsl:if test="ai_http_proxy/@url">
+                <xsl:attribute name="http_proxy">
+                    <xsl:value-of select="normalize-space(ai_http_proxy/@url)"/>
+                </xsl:attribute>
+            </xsl:if>
+
+            <xsl:call-template name="target_swap_dump_partitioning_slices"/>
+
+            <xsl:call-template name="install_uninstall_repo"/>
+
+            <xsl:apply-templates select="ai_add_drivers"/>
+
+            <!-- Handle sc_embedded_manifest, where ai_manifest is also present -->
+            <xsl:if test="../../sc_embedded_manifest">
+                <xsl:copy-of select="../../sc_embedded_manifest"/>
+            </xsl:if>
+        </ai_instance>
+    </xsl:template>
+
+
+    <!-- Handle following elements from -->
+    <!-- the old schema which all transform to parts of the -->
+    <!-- target tree in the new schema: -->
+    <!-- ai_target_device, ai_swap_device, ai_dump_device, -->
+    <!-- ai_device_partitioning, ai_device_vtoc_slices. -->
+
+    <xsl:template name="target_swap_dump_partitioning_slices">
+        <xsl:if test="ai_target_device or ai_swap_device or ai_dump_device or ai_device_partitioning or ai_device_vtoc_slices">
+            <target>
+                <xsl:if test="ai_target_device or ai_device_partitioning">
+                    <target_device>
+                        <xsl:call-template name="target_partitioning_slices"/>
+                    </target_device>
+                </xsl:if>
+
+                <xsl:apply-templates select="ai_swap_device"/>
+                <xsl:apply-templates select="ai_dump_device"/>
+            </target>
+        </xsl:if>
+    </xsl:template>
+
+
+    <!-- Handle: -->
+    <!-- ai_target_device, -->
+    <!-- ai_device_partitioning -->
+    <!-- ai_device_vtoc_slices -->
+    <!-- elements -->
+
+    <xsl:template name="target_partitioning_slices">
+        <xsl:if test="ai_target_device or ai_device_partitioning or ai_device_vtoc_slices">
+            <disk>
+                <xsl:apply-templates select="ai_target_device/target_device_name"/>
+                <xsl:apply-templates select="ai_target_device/target_device_select_volume_name"/>
+                <xsl:apply-templates select="ai_target_device/target_device_select_id"/>
+                <xsl:apply-templates select="ai_target_device/target_device_select_device_path"/>
+
+                <!-- The following elements are handled together: -->
+                <!-- target_device_type, target_device_size, -->
+                <!-- target_device_vendor -->
+                <xsl:call-template name="target_device_type_size_vendor"/>
+
+                <xsl:apply-templates select="ai_target_device/target_device_use_solaris_partition"/>
+
+                <!-- All the target_device_iscsi_* elements -->
+                <!-- are handled together. -->
+                <xsl:call-template name="target_device_iscsi"/>
+
+                <xsl:apply-templates select="ai_device_partitioning"/>
+                <xsl:apply-templates select="ai_device_vtoc_slices"/>
+            </disk>
+        </xsl:if>
+    </xsl:template>
+
+
+    <xsl:template match="ai_target_device/target_device_name">
+        <xsl:choose>
+            <xsl:when test="normalize-space(text()) = 'boot_disk'">
+                <disk_keyword>
+                    <xsl:attribute name="key">
+                        <xsl:value-of select="normalize-space(text())"/>
+                    </xsl:attribute>
+                </disk_keyword>
+            </xsl:when>
+            <xsl:otherwise>
+                <disk_name name_type='ctd'>
+                    <xsl:attribute name="name">
+                        <xsl:value-of select="normalize-space(text())"/>
+                    </xsl:attribute>
+                </disk_name>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template match="ai_target_device/target_device_select_volume_name">
+        <disk_name name_type='volid'>
+            <xsl:attribute name="name">
+                <xsl:value-of select="normalize-space(text())"/>
+            </xsl:attribute>
+        </disk_name>
+    </xsl:template>
+
+    <xsl:template match="ai_target_device/target_device_select_id">
+        <disk_name name_type='devid'>
+            <xsl:attribute name="name">
+                <xsl:value-of select="normalize-space(text())"/>
+            </xsl:attribute>
+        </disk_name>
+    </xsl:template>
+
+    <xsl:template match="ai_target_device/target_device_select_device_path">
+        <disk_name name_type='devpath'>
+            <xsl:attribute name="name">
+                <xsl:value-of select="normalize-space(text())"/>
+            </xsl:attribute>
+        </disk_name>
+    </xsl:template>
+
+
+    <!-- More than one of the following elements can be specified: -->
+    <!-- target_device_type, target_device_size, target_device_vendor -->
+    <!-- and each one corresponds to an attribute on a single -->
+    <!-- disk_prop element in the new schema. -->
+
+    <xsl:template name="target_device_type_size_vendor">
+        <xsl:if test="ai_target_device/target_device_type or ai_target_device/target_device_size or ai_target_device/target_device_vendor">
+            <disk_prop>
+                <xsl:if test="ai_target_device/target_device_type">
+                    <xsl:attribute name="dev_type">
+                        <xsl:value-of select="normalize-space(ai_target_device/target_device_type)"/>
+                    </xsl:attribute>
+                </xsl:if>
+                <xsl:if test="ai_target_device/target_device_size">
+                    <xsl:attribute name="dev_size">
+                        <xsl:value-of select="normalize-space(ai_target_device/target_device_size)"/>
+                        <!-- Old values were assumed to be in 'sectors'.  -->
+                        <xsl:text>Sec</xsl:text>
+                    </xsl:attribute>
+                </xsl:if>
+                <xsl:if test="ai_target_device/target_device_vendor">
+                    <xsl:attribute name="dev_vendor">
+                        <xsl:value-of select="normalize-space(ai_target_device/target_device_vendor)"/>
+                    </xsl:attribute>
+                </xsl:if>
+            </disk_prop>
+        </xsl:if>
+    </xsl:template>
+
+    <!-- FIXME: does this need to be combined with ai_device_partitioning? -->
+    <xsl:template match="ai_target_device/target_device_use_solaris_partition">
+        <xsl:variable name="use_partition_val"
+            select="translate(text(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
+        <xsl:if test="normalize-space($use_partition_val) = true">
+            <partition action='use_existing'/>
+        </xsl:if>
+    </xsl:template>
+
+    <!-- The following are separate elements in old but -->
+    <!-- correspond to attributes or sub-elements of a -->
+    <!-- single iscsi element in new: -->
+    <!-- target_device_iscsi_target_name, target_device_iscsi_target_ip, -->
+    <!-- target_device_iscsi_target_lun, target_device_iscsi_target_port, -->
+    <!-- target_device_iscsi_parameter_source. -->
+
+    <xsl:template name="target_device_iscsi">
+        <xsl:if test="ai_target_device/target_device_iscsi_target_name or ai_target_device/target_device_iscsi_target_ip or ai_target_device/target_device_iscsi_target_lun or ai_target_device/target_device_iscsi_target_port or ai_target_device/target_device_iscsi_parameter_source">
+            <iscsi>
+                <xsl:if test="ai_target_device/target_device_iscsi_target_name">
+                    <xsl:attribute name="name">
+                        <xsl:value-of select="normalize-space(ai_target_device/target_device_iscsi_target_name)"/>
+                    </xsl:attribute>
+                </xsl:if>
+                <xsl:if test="ai_target_device/target_device_iscsi_target_lun">
+                    <xsl:attribute name="target_lun">
+                        <xsl:value-of select="normalize-space(ai_target_device/target_device_iscsi_target_lun)"/>
+                    </xsl:attribute>
+                </xsl:if>
+                <xsl:if test="ai_target_device/target_device_iscsi_target_port">
+                    <xsl:attribute name="target_port">
+                        <xsl:value-of select="normalize-space(ai_target_device/target_device_iscsi_target_port)"/>
+                    </xsl:attribute>
+                </xsl:if>
+                <xsl:if test="ai_target_device/target_device_iscsi_parameter_source">
+                    <xsl:attribute name="source">
+                        <xsl:value-of select="normalize-space(ai_target_device/target_device_iscsi_parameter_source)"/>
+                    </xsl:attribute>
+                </xsl:if>
+                <!-- 'ip' must be processed last as it transforms to a -->
+                <!-- sub-element and attributes must be completed before -->
+                <!-- sub-elements can be added. -->
+                <xsl:if test="ai_target_device/target_device_iscsi_target_ip">
+                    <ip>
+                        <xsl:value-of select="normalize-space(ai_target_device/target_device_iscsi_target_ip)"/>
+                    </ip>
+                </xsl:if>
+            </iscsi>
+        </xsl:if>
+    </xsl:template>
+
+
+    <!-- Handle ai_swap_device -->
+
+    <xsl:template match="ai_swap_device">
+        <target_device>
+            <swap>
+                <zvol action="create" name="swap">
+                    <xsl:apply-templates select="ai_swap_size"/>
+                </zvol>
+            </swap>
+        </target_device>
+    </xsl:template>
+
+    <xsl:template match="ai_swap_size">
+        <size>
+            <xsl:attribute name="val">
+                <xsl:value-of select="normalize-space(text())"/>
+                <!-- Old values were assumed to be in mb.  -->
+                <xsl:text>mb</xsl:text>
+            </xsl:attribute>
+        </size>
+    </xsl:template>
+
+
+    <!-- Handle ai_dump_device -->
+
+    <xsl:template match="ai_dump_device">
+        <target_device>
+            <dump>
+                <zvol action="create" name="dump">
+                    <xsl:apply-templates select="ai_dump_size"/>
+                </zvol>
+            </dump>
+        </target_device>
+    </xsl:template>
+
+    <xsl:template match="ai_dump_size">
+        <size>
+            <xsl:attribute name="val">
+                <xsl:value-of select="normalize-space(text())"/>
+                <!-- Old values were assumed to be in mb.  -->
+                <xsl:text>mb</xsl:text>
+            </xsl:attribute>
+        </size>
+    </xsl:template>
+
+
+    <!-- handle ai_device_partitioning -->
+
+    <xsl:template match="ai_device_partitioning">
+        <partition>
+            <xsl:if test="partition_action">
+                <xsl:attribute name="action">
+                    <xsl:value-of select="normalize-space(partition_action)"/>
+                </xsl:attribute>
+            </xsl:if>
+            <xsl:if test="partition_number">
+                <xsl:attribute name="name">
+                    <xsl:value-of select="normalize-space(partition_number)"/>
+                </xsl:attribute>
+            </xsl:if>
+            <xsl:if test="partition_type">
+                <xsl:attribute name="part_type">
+                    <xsl:value-of select="normalize-space(partition_type)"/>
+                </xsl:attribute>
+            </xsl:if>
+            <xsl:if test="partition_size">
+                <size>
+                    <xsl:attribute name="val">
+                        <xsl:variable name="size_val"
+                            select="translate(partition_size, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
+                        <xsl:choose>
+                            <xsl:when test="normalize-space($size_val) = 'MAX_SIZE'">
+                                <xsl:text>0</xsl:text>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:value-of select="normalize-space($size_val)"/>
+                            </xsl:otherwise>
+                        </xsl:choose>
+
+                        <xsl:choose>
+                            <xsl:when test="partition_size_units">
+                                <xsl:value-of select="normalize-space(partition_size_units)"/>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <!-- If no size units given, default to mb.  -->
+                                <xsl:text>mb</xsl:text>
+                            </xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:attribute>
+
+                    <!-- This is for when partition_start_sector is -->
+                    <!-- specified along with partition_size. -->
+                    <xsl:if test="partition_start_sector">
+                        <xsl:attribute name="start_sector">
+                            <xsl:value-of select="normalize-space(partition_start_sector)"/>
+                        </xsl:attribute>
+                    </xsl:if>
+                </size>
+            </xsl:if>
+            <xsl:if test="not(partition_size)">
+                <!-- This is for when partition_start_sector is -->
+                <!-- specified WITHOUT partition_size. (Should only -->
+                <!-- happen for action=delete.) -->
+                <xsl:if test="partition_start_sector">
+                    <size>
+                        <!-- size/val is mandatory, so use 0. -->
+                        <xsl:attribute name="val">
+                            <xsl:text>0</xsl:text>
+                        </xsl:attribute>
+                        <xsl:attribute name="start_sector">
+                            <xsl:value-of select="normalize-space(partition_start_sector)"/>
+                        </xsl:attribute>
+                    </size>
+                </xsl:if>
+            </xsl:if>
+        </partition>
+    </xsl:template>
+
+
+    <!-- handle ai_device_vtoc_slices -->
+
+    <xsl:template match="ai_device_vtoc_slices">
+        <slice>
+            <xsl:if test="slice_action">
+                <xsl:attribute name="action">
+                    <xsl:value-of select="normalize-space(slice_action)"/>
+                </xsl:attribute>
+            </xsl:if>
+            <xsl:if test="slice_number">
+                <xsl:attribute name="name">
+                    <xsl:value-of select="normalize-space(slice_number)"/>
+                </xsl:attribute>
+
+                <!-- if manifest specified a -->
+                <!-- target_device_install_slice_number value and -->
+                <!-- it is the same as the current slice_number then -->
+                <!-- set attribute is_root="true" on current slice. -->
+                <xsl:if test="/ai_manifest/ai_target_device/target_device_install_slice_number">
+                    <xsl:if test="normalize-space(/ai_manifest/ai_target_device/target_device_install_slice_number) = normalize-space(slice_number)">
+                        <xsl:attribute name="is_root">
+                            <xsl:text>true</xsl:text>
+                        </xsl:attribute>
+                    </xsl:if>
+                </xsl:if>
+            </xsl:if>
+            <xsl:if test="slice_on_existing">
+                <xsl:attribute name="force">
+                    <xsl:variable name="on_existing_upr"
+                        select="translate(slice_on_existing, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
+                    <xsl:choose>
+                        <xsl:when test="normalize-space($on_existing_upr) = 'OVERWRITE'">
+                            <xsl:text>true</xsl:text>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:text>false</xsl:text>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                </xsl:attribute>
+            </xsl:if>
+            <xsl:if test="slice_size">
+                <size>
+                    <xsl:attribute name="val">
+                        <xsl:variable name="size_val"
+                            select="translate(slice_size, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
+                        <xsl:choose>
+                            <xsl:when test="normalize-space($size_val) = 'MAX_SIZE'">
+                                <xsl:text>0</xsl:text>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:value-of select="normalize-space($size_val)"/>
+                            </xsl:otherwise>
+                        </xsl:choose>
+
+                        <xsl:choose>
+                            <xsl:when test="slice_size_units">
+                                <xsl:value-of select="normalize-space(slice_size_units)"/>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <!-- If no size units given, default to mb.  -->
+                                <xsl:text>mb</xsl:text>
+                            </xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:attribute>
+                </size>
+            </xsl:if>
+        </slice>
+    </xsl:template>
+
+
+    <!-- Handle all the ai_*_packages  and ai_pkg_repo_* elements -->
+
+    <xsl:template name="install_uninstall_repo">
+        <xsl:if test="ai_install_packages or ai_uninstall_packages or ai_pkg_repo_default_publisher or ai_pkg_repo_default_authority or ai_pkg_repo_addl_publisher or ai_pkg_repo_addl_authority">
+            <software>
+                <xsl:call-template name="repos"/>
+
+                <xsl:apply-templates select="ai_packages"/>
+                <xsl:apply-templates select="ai_install_packages"/>
+                <xsl:apply-templates select="ai_uninstall_packages"/>
+            </software>
+        </xsl:if>
+    </xsl:template>
+
+
+    <!-- Handle ai_*_packages -->
+
+    <xsl:template match="ai_packages">
+        <software_data>
+            <xsl:attribute name="action">
+                <xsl:text>install</xsl:text>
+            </xsl:attribute>
+
+            <xsl:apply-templates select="pkg"/>
+        </software_data>
+    </xsl:template>
+
+    <xsl:template match="ai_install_packages">
+        <software_data>
+            <xsl:attribute name="action">
+                <xsl:text>install</xsl:text>
+            </xsl:attribute>
+
+            <xsl:apply-templates select="pkg"/>
+        </software_data>
+    </xsl:template>
+
+    <xsl:template match="ai_uninstall_packages">
+        <software_data>
+            <xsl:attribute name="action">
+                <xsl:text>uninstall</xsl:text>
+            </xsl:attribute>
+
+            <xsl:apply-templates select="pkg"/>
+        </software_data>
+    </xsl:template>
+
+    <xsl:template match="pkg">
+        <xsl:if test="@name">
+            <!-- If pkg name was a comma-separated list, break it up into pkg names. -->
+            <xsl:call-template name="package_name_list">
+                <xsl:with-param name="str" select="@name"/>
+                <xsl:with-param name="splitString" select="','"/>
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:template>
+
+
+    <!-- Handle all the ai_pkg_repo_* elements -->
+
+    <xsl:template name="repos">
+        <xsl:if test="ai_pkg_repo_default_publisher or ai_pkg_repo_default_authority or ai_pkg_repo_addl_publisher or ai_pkg_repo_addl_authority">
+            <source>
+                <xsl:apply-templates select="ai_pkg_repo_default_publisher"/>
+                <xsl:apply-templates select="ai_pkg_repo_default_authority"/>
+                <xsl:apply-templates select="ai_pkg_repo_addl_publisher"/>
+                <xsl:apply-templates select="ai_pkg_repo_addl_authority"/>
+            </source>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="ai_pkg_repo_default_publisher">
+        <xsl:apply-templates select="main"/>
+    </xsl:template>
+
+    <xsl:template match="ai_pkg_repo_default_authority">
+        <xsl:apply-templates select="main"/>
+    </xsl:template>
+
+    <xsl:template match="ai_pkg_repo_addl_publisher">
+        <xsl:apply-templates select="main"/>
+    </xsl:template>
+
+    <xsl:template match="ai_pkg_repo_addl_authority">
+        <xsl:apply-templates select="main"/>
+    </xsl:template>
+
+    <xsl:template match="main">
+        <publisher>
+            <xsl:if test="@publisher">
+                <xsl:attribute name="name">
+                    <xsl:value-of select="normalize-space(@publisher)"/>
+                </xsl:attribute>
+            </xsl:if>
+            <xsl:if test="@authname">
+                <xsl:attribute name="name">
+                    <xsl:value-of select="normalize-space(@authname)"/>
+                </xsl:attribute>
+            </xsl:if>
+            <xsl:if test="@url">
+                <origin>
+                    <xsl:attribute name="name">
+                        <xsl:value-of select="normalize-space(@url)"/>
+                    </xsl:attribute>
+                </origin>
+            </xsl:if>
+            <!-- This assumes that mirror will not be present unless -->
+            <!-- corresponding main is also present. -->
+            <xsl:if test="../mirror/@url">
+                <mirror>
+                    <xsl:attribute name="name">
+                        <xsl:value-of select="normalize-space(../mirror/@url)"/>
+                    </xsl:attribute>
+                </mirror>
+            </xsl:if>
+        </publisher>
+    </xsl:template>
+
+
+    <!-- Handle ai_add_drivers -->
+
+    <xsl:template match="ai_add_drivers">
+        <add_drivers>
+            <xsl:apply-templates select="bundle"/>
+            <xsl:apply-templates select="searchall"/>
+        </add_drivers>
+    </xsl:template>
+
+    <xsl:template match="bundle">
+        <software>
+            <source>
+                <xsl:if test="@location">
+                    <publisher>
+                        <origin>
+                            <xsl:attribute name="name">
+                                <xsl:value-of select="normalize-space(@location)"/>
+                            </xsl:attribute>
+                        </origin>
+                    </publisher>
+                </xsl:if>
+            </source>
+
+            <xsl:if test="@type or @noinstall or @name">
+                <software_data>
+                    <xsl:if test="@type">
+                        <xsl:attribute name="type">
+                            <xsl:value-of select="normalize-space(@type)"/>
+                        </xsl:attribute>
+                    </xsl:if>
+
+                    <xsl:if test="@noinstall">
+                        <xsl:variable name="noinstall_upr"
+                            select="translate(@noinstall, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
+                        <xsl:choose>
+                            <xsl:when test="$noinstall_upr = 'TRUE'">
+                                <xsl:attribute name="action">
+                                    <xsl:text>noinstall</xsl:text>
+                                </xsl:attribute>
+                            </xsl:when>
+                        </xsl:choose>
+                    </xsl:if>
+
+                    <xsl:if test="@name">
+                        <!-- If pkg name was a comma-separated list, break it up into pkg names. -->
+                        <xsl:call-template name="package_name_list">
+                            <xsl:with-param name="str" select="@name"/>
+                            <xsl:with-param name="splitString" select="','"/>
+                        </xsl:call-template>
+                    </xsl:if>
+                </software_data>
+            </xsl:if>
+        </software>
+    </xsl:template>
+
+    <xsl:template match="searchall">
+        <search_all>
+            <xsl:if test="@addall">
+                <xsl:attribute name="addall">
+                    <xsl:value-of select="@addall"/>
+                </xsl:attribute>
+            </xsl:if>
+
+            <xsl:if test="@location or @publisher">
+                <source>
+                    <publisher>
+                        <xsl:if test="@publisher">
+                            <xsl:attribute name="name">
+                                <xsl:value-of select="@publisher"/>
+                            </xsl:attribute>
+                        </xsl:if>
+
+                        <xsl:if test="@location">
+                            <origin>
+                                <xsl:attribute name="name">
+                                    <xsl:value-of select="@location"/>
+                                </xsl:attribute>
+                            </origin>
+                        </xsl:if>
+                    </publisher>
+                </source>
+            </xsl:if>
+        </search_all>
+    </xsl:template>
+
+
+    <!-- Handle sc_embedded_manifest, where ai_manifest is *not* present. -->
+    <!-- NB: this output won't validate, but may be of some when porting. -->
+    <xsl:template match="sc_embedded_manifest">
+        <xsl:if test="not(../ai_embedded_manifest/ai_manifest)">
+            <ai_instance name="auto_install">
+                <xsl:copy-of select="."/>
+            </ai_instance>
+        </xsl:if>
+    </xsl:template>
+
+
+
+    <!-- Recursive tokenizing template for splitting a -->
+    <!-- comma-separated list of packages names into a -->
+    <!-- list of name elements. -->
+    <!-- Written by David Pawson, found at: -->
+    <!-- http://www.oxygenxml.com/archives/xsl-list/200504/msg00939.html -->
+
+    <xsl:template name="package_name_list">
+        <xsl:param name="str" select="."/>
+        <xsl:param name="splitString" select="','"/>
+        <xsl:choose>
+            <xsl:when test="contains($str,$splitString)">
+                <name>
+                    <xsl:value-of select="normalize-space(substring-before($str,$splitString))"/>
+                </name>
+                <xsl:call-template name="package_name_list">
+                    <xsl:with-param name="str" select="substring-after($str,$splitString)"/>
+                    <xsl:with-param name="splitString" select="$splitString"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                <name><xsl:value-of select="normalize-space($str)"/></name>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+</xsl:stylesheet>
--- a/usr/src/cmd/distro_const/text_install/text_live.xml	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/distro_const/text_install/text_live.xml	Fri Aug 20 11:31:18 2010 -0600
@@ -20,7 +20,7 @@
    
     CDDL HEADER END
    
-    Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 
     Service profile customization for Text Installer image
 -->
--- a/usr/src/cmd/gui-install/src/orchestrator-wrappers.c	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/gui-install/src/orchestrator-wrappers.c	Fri Aug 20 11:31:18 2010 -0600
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifdef HAVE_CONFIG_H
--- a/usr/src/cmd/gui-install/src/orchestrator-wrappers.h	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/gui-install/src/orchestrator-wrappers.h	Fri Aug 20 11:31:18 2010 -0600
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef __ORCHESTRATOR_WRAPPERS_H
--- a/usr/src/cmd/install-tools/ManifestServ.py	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/install-tools/ManifestServ.py	Fri Aug 20 11:31:18 2010 -0600
@@ -20,8 +20,10 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
 
 # =============================================================================
 # =============================================================================
@@ -135,6 +137,7 @@
                       "<manifest_basename>_temp_<pid>")
     print >> msg_fd, "  -v: verbose defaults/validation output"
     print >> msg_fd, ("  -s: start socket server for use by ManifestRead")
+    print >> msg_fd, ("  --dtd: use DTD validation (default is RelaxNG)")
 
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -169,7 +172,7 @@
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
     # Initialize option flags.
-    d_flag = s_flag = t_flag = v_flag = False
+    d_flag = s_flag = t_flag = v_flag = dtd_flag = False
 
     mfest_obj = None
     err = None
@@ -178,7 +181,7 @@
     # Options come first in the commandline.
     # See usage method for option explanations.
     try:
-        (opt_pairs, other_args) = getopt.getopt(sys.argv[1:], "df:ho:stv?")
+        (opt_pairs, other_args) = getopt.getopt(sys.argv[1:], "df:ho:stv?", "dtd")
     except getopt.GetoptError, err:
         print >> sys.stderr, "ManifestServ: " + str(err)
     except IndexError, err:
@@ -205,6 +208,8 @@
             t_flag = True
         elif (opt == "-v"):
             v_flag = True
+        elif (opt == "--dtd"):
+            dtd_flag = True
 
     # Must have the project data manifest.
     # Also check for mismatching options.
@@ -215,7 +220,7 @@
     try:
         # Create the object used to extract the data.
         mfest_obj = ManifestServ(other_args[0], valfile_root,
-                                 out_manifest, v_flag, t_flag)
+                                 out_manifest, v_flag, t_flag, dtd_schema=dtd_flag)
 
         # Start the socket server if requested.
         if (s_flag):
--- a/usr/src/cmd/installadm/installadm.c	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/installadm/installadm.c	Fri Aug 20 11:31:18 2010 -0600
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <stdio.h>
@@ -49,7 +48,8 @@
 static cmdfunc_t do_create_service, do_delete_service;
 static cmdfunc_t do_list, do_enable, do_disable;
 static cmdfunc_t do_create_client, do_delete_client;
-static cmdfunc_t do_add, do_remove, do_help;
+static cmdfunc_t do_add_manifest, do_delete_manifest;
+static cmdfunc_t do_set_criteria, do_help;
 static void do_opterr(int, int, const char *);
 static char *progname;
 static void smf_service_enable_attempt(char *);
@@ -63,6 +63,7 @@
 	char		*c_name;
 	cmdfunc_t	*c_fn;
 	const char	*c_usage;
+	char		*c_alias;
 	boolean_t	c_priv_reqd;
 } cmd_t;
 
@@ -72,43 +73,60 @@
 	    "\t\t\t[-f <bootfile>] [-n <svcname>]\n"
 	    "\t\t\t[-i <dhcp_ip_start>] [-c <count_of_ipaddr>]\n"
 	    "\t\t\t[-s <srcimage>] <targetdir>",
+	    "create-service",
 	    PRIV_REQD							},
 
 	{ "delete-service",	do_delete_service,
 	    "\tdelete-service\t[-x] <svcname>",
+	    "delete-service",
 	    PRIV_REQD							},
 
 	{ "list",	do_list,
 	    "\tlist\t[-n <svcname>] [-c] [-m]",
+	    "list",
 	    PRIV_NOT_REQD						},
 
 	{ "enable",	do_enable,
 	    "\tenable\t<svcname>",
+	    "enable",
 	    PRIV_REQD							},
 
 	{ "disable",	do_disable,
 	    "\tdisable\t[-t] <svcname>",
+	    "disable",
 	    PRIV_REQD							},
 
 	{ "create-client",	do_create_client,
 	    "\tcreate-client\t[-b <property>=<value>,...] \n"
 	    "\t\t\t-e <macaddr> -n <svcname> [-t <imagepath>]",
+	    "create-client",
 	    PRIV_REQD							},
 
 	{ "delete-client",	do_delete_client,
 	    "\tdelete-client\t<macaddr>",
+	    "delete-client",
+	    PRIV_REQD							},
+
+	{ "add-manifest",	do_add_manifest,
+	    "\tadd-manifest\t-m <manifest> -n <svcname>\n"
+	    "\t\t\t[-c <criteria=value|range> ... | -C <criteria.xml>]",
+	    "add",
 	    PRIV_REQD							},
 
-	{ "add",	do_add,
-	    "\tadd\t-m <manifest> -n <svcname>",
+	{ "delete-manifest",	do_delete_manifest,
+	    "\tdelete-manifest\t-m <manifest> -n <svcname>",
+	    "remove",
 	    PRIV_REQD							},
 
-	{ "remove",	do_remove,
-	    "\tremove\t-m <manifest> -n <svcname>",
+	{ "set-criteria",	do_set_criteria,
+	    "\tset-criteria\t-m <manifest> -n <svcname> \n"
+	    "\t\t\t-a|-c <criteria=value|range> ... | -C <criteria.xml>",
+	    "set-criteria",
 	    PRIV_REQD							},
 
 	{ "help",	do_help,
 	    "\thelp\t[<subcommand>]",
+	    "help",
 	    PRIV_NOT_REQD						}
 };
 
@@ -155,7 +173,8 @@
 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
 		int ret = 0;
 		cmdp = &cmds[i];
-		if (strcmp(argv[1], cmdp->c_name) == 0) {
+		if (strcmp(argv[1], cmdp->c_name) == 0 ||
+			strcmp(argv[1], cmdp->c_alias) == 0) {
 			if ((cmdp->c_priv_reqd) && (geteuid() > 0)) {
 				(void) fprintf(stderr, MSG_ROOT_PRIVS_REQD,
 				    argv[0], cmdp->c_name);
@@ -1207,65 +1226,21 @@
 }
 
 /*
- * do_add:
- * Add manifests to an A/I service
- * Parse command line for criteria manifest and service name; get service
- * directory path from service name; then pass manifest and service directory
- * path to publish-manifest(1)
+ * do_add_manifest:
+ * Add manifest to an A/I service
+ * Pass all command line options to publish_manifest
  */
 static int
-do_add(int argc, char *argv[], scfutilhandle_t *handle, const char *use)
+do_add_manifest(int argc, char *argv[], scfutilhandle_t *handle,
+		const char *use)
 {
-	int	option = NULL;
-	char	*manifest = NULL;
-	char	*svcname = NULL;
-	char	cmd[MAXPATHLEN];
 	int	ret;
 
-	/*
-	 * Check for valid number of arguments
-	 */
-	if (argc != 5) {
-		(void) fprintf(stderr, "%s\n", gettext(use));
-		return (INSTALLADM_FAILURE);
-	}
-
-	while ((option = getopt(argc, argv, ":n:m:")) != -1) {
-		switch (option) {
-			case 'n':
-				svcname = optarg;
-				break;
-			case 'm':
-				manifest = optarg;
-				break;
-			default:
-				do_opterr(optopt, option, use);
-				return (INSTALLADM_FAILURE);
-		}
-	}
-
-	/*
-	 * Make sure required options are there
-	 */
-	if ((svcname == NULL) || (manifest == NULL)) {
-		(void) fprintf(stderr, MSG_MISSING_OPTIONS, argv[0]);
-		(void) fprintf(stderr, "%s\n", gettext(use));
-		return (INSTALLADM_FAILURE);
-	}
-
-	if (!validate_service_name(svcname)) {
-		(void) fprintf(stderr, MSG_BAD_SERVICE_NAME);
-		return (INSTALLADM_FAILURE);
-	}
-
-	(void) snprintf(cmd, sizeof (cmd), "%s %s %s",
-	    MANIFEST_MODIFY_SCRIPT, svcname, manifest);
-
-	ret = installadm_system(cmd);
+	ret = call_script(MANIFEST_MODIFY_SCRIPT, argc-1, &argv[1]);
 
 	/*
 	 * Ensure we return an error if ret != 0.
-	 * If WEXITSTATUS(ret) == 1 then the Python handled the error,
+	 * If WEXITSTATUS(ret) == 1 then Python handled the error,
 	 * do not print a new error.
 	 */
 	if (ret != 0) {
@@ -1279,7 +1254,7 @@
 }
 
 /*
- * do_remove:
+ * do_delete_manifest:
  * Remove manifests from an A/I service
  * Parse the command line for service name and manifest name (and if
  * provided, internal instance name); then, get the service directory
@@ -1288,7 +1263,8 @@
  * delete-manifest(1)
  */
 static int
-do_remove(int argc, char *argv[], scfutilhandle_t *handle, const char *use)
+do_delete_manifest(int argc, char *argv[], scfutilhandle_t *handle,
+			const char *use)
 {
 	int	option;
 	char	*port = NULL;
@@ -1382,7 +1358,7 @@
 
 	/*
 	 * Ensure we return an error if ret != 0.
-	 * If WEXITSTATUS(ret) == 1 then the Python handled the error,
+	 * If WEXITSTATUS(ret) == 1 then Python handled the error,
 	 * do not print a new error.
 	 */
 	if (ret != 0) {
@@ -1396,6 +1372,34 @@
 	return (INSTALLADM_SUCCESS);
 }
 
+/*
+ * do_set_criteria:
+ * Set criteria for an already published AI manifest.
+ * Pass all command line options to set_criteria
+ */
+static int
+do_set_criteria(int argc, char *argv[], scfutilhandle_t *handle,
+		const char *use)
+{
+	int		ret;
+
+	ret = call_script(SET_CRITERIA_SCRIPT, argc-1, &argv[1]);
+
+	/*
+	 * Ensure we return an error if ret != 0.
+	 * If WEXITSTATUS(ret) == 1 then Python handled the error,
+	 * do not print a new error.
+	 */
+	if (ret != 0) {
+		if (WEXITSTATUS(ret) == 1) {
+			return (INSTALLADM_FAILURE);
+		}
+		(void) fprintf(stderr, MSG_SUBCOMMAND_FAILED, argv[0]);
+		return (INSTALLADM_FAILURE);
+	}
+	return (INSTALLADM_SUCCESS);
+}
+
 static int
 do_help(int argc, char *argv[], scfutilhandle_t *handle, const char *use)
 {
--- a/usr/src/cmd/installadm/installadm.h	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/installadm/installadm.h	Fri Aug 20 11:31:18 2010 -0600
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_INSTALLADM_H
@@ -49,10 +48,10 @@
 #define	SERVICE_LIST		"list"
 
 #define	MANIFEST_REMOVE_SCRIPT	"/usr/lib/installadm/delete-manifest"
-#define	MANIFEST_MODIFY_SCRIPT	"/usr/lib/installadm/publish-manifest"
+#define	MANIFEST_MODIFY_SCRIPT	"/usr/lib/installadm/publish_manifest.py"
+#define	SET_CRITERIA_SCRIPT	"/usr/lib/installadm/set_criteria.py"
 
 #define	LIST_SCRIPT		"/usr/lib/installadm/list"
-
 #define	CREATE_CLIENT_SCRIPT	"/usr/lib/installadm/create-client"
 #define	DELETE_CLIENT_SCRIPT	"/usr/lib/installadm/delete-client"
 
@@ -136,6 +135,8 @@
 	"%s %s: unknown subcommand '%s'.\n")
 #define	MSG_MISSING_OPTIONS	INSTALLADMSTR(\
 	"%s: missing one or more required options.\nusage:\n")
+#define	MSG_EXCLUSIVE_OPTIONS	INSTALLADMSTR(\
+	"%s: options used are mutually exclusive.\nusage:\n")
 #define	MSG_OPTION_NOHELP	INSTALLADMSTR(\
 	"%s %s: No help available for subcommand '%s'\n")
 #define	MSG_OPTION_VALUE_MISSING	INSTALLADMSTR(\
--- a/usr/src/cmd/installadm/list.py	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/installadm/list.py	Fri Aug 20 11:31:18 2010 -0600
@@ -19,8 +19,8 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+#
 """
 
 A/I List Services
@@ -824,7 +824,7 @@
                     'platform':'', 'network':'', 'cpu':''}
         twidth = 0
         for key in mancriteria.keys():
-            if not mancriteria[key] or mancriteria[key] == '':
+            if mancriteria[key] is None or mancriteria[key] == '':
                 continue # no criteria for instance key
             twidth = max(twidth, len(key.lstrip('MAX').lstrip('MIN')))
             svalue = AIdb.formatValue(key, mancriteria[key])
--- a/usr/src/lib/install_utils/DefValProc.py	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/lib/install_utils/DefValProc.py	Fri Aug 20 11:31:18 2010 -0600
@@ -18,8 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 
 # =============================================================================
 # =============================================================================
@@ -47,7 +46,10 @@
 SUCCESS = 0
 
 # XML validator program, run on XML docs to validate against a schema.
-XML_VALIDATOR = "/bin/xmllint --relaxng "
+XML_VALIDATOR = "/bin/xmllint"
+XML_RNG_SCHEMA = "--relaxng"
+XML_DTD_SCHEMA = "--dtdvalid"
+XML_DTD_DEFAULTS = "--dtdattr"
 XML_REFORMAT_SW = "--format"
 
 # Defaults and validation manifest doc and schema filenames.
@@ -240,7 +242,8 @@
 # =============================================================================
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def __validate_vs_schema(schema, in_xml_doc, out_xml_doc=None):
+def __validate_vs_schema(schema, in_xml_doc, out_xml_doc=None,
+                         dtd_schema=False):
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     """ Validate an XML document against a schema.
 
@@ -255,6 +258,9 @@
 
       out_xml_doc: Reformatted XML doc
 
+      dtd_schema: Optional. Defaults to False.
+        If True, validate against DTD Schema file.  If False, use RNG.
+
     Returns: N/A
 
     Raises:
@@ -275,8 +281,15 @@
     canaccess(schema, "r")
     canaccess(in_xml_doc, "r")
 
-    command_list = XML_VALIDATOR.split()
-    command_list.append(schema)
+    command_list = [XML_VALIDATOR]
+
+    if dtd_schema:
+        command_list.append(XML_DTD_SCHEMA)
+        command_list.append(schema)
+        command_list.append(XML_DTD_DEFAULTS)
+    else:
+        command_list.append(XML_RNG_SCHEMA)
+        command_list.append(schema)
 
     if (out_xml_doc is not None):
         command_list.append(XML_REFORMAT_SW)
@@ -1285,7 +1298,8 @@
 
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def schema_validate(schema_file, in_dc_manifest, out_dc_manifest=None):
+def schema_validate(schema_file, in_dc_manifest, out_dc_manifest=None,
+                    dtd_schema=False):
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     """ Validate a DC-manifest against its schema.
 
@@ -1296,6 +1310,9 @@
 
       out_dc_manifest: Name of reformatted file of in_dc_manifest.
 
+      dtd_schema: Optional. Defaults to False.
+        If True, validate against DTD Schema file.  If False, use RNG.
+
     Returns: N/A
 
     Raises:
@@ -1304,7 +1321,8 @@
     """
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     try:
-        __validate_vs_schema(schema_file, in_dc_manifest, out_dc_manifest)
+        __validate_vs_schema(schema_file, in_dc_manifest, out_dc_manifest,
+                             dtd_schema=dtd_schema)
     except StandardError, err:
         print >> sys.stderr, str(err)
         raise ManifestProcError, ("schema_validate: Schema validation " +
--- a/usr/src/lib/install_utils/ManifestServ.py	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/lib/install_utils/ManifestServ.py	Fri Aug 20 11:31:18 2010 -0600
@@ -73,6 +73,7 @@
 
     # Project manifest schema against which P.M. XML file is validated.
     SCHEMA_SUFFIX = ".rng"
+    DTD_SCHEMA_SUFFIX = ".dtd"
 
     # Defaults and content validation XML file, defining defaults and how
     # to validate the project manifest for symantics/content.
@@ -81,7 +82,8 @@
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     def __init__(self, manifest_name, valfile_base=None,
                  out_manifest_name=None, verbose=False,
-                 keep_temp_files=False, full_init=True):
+                 keep_temp_files=False, full_init=True,
+                 dtd_schema=False):
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         """ Constructor.  Initialize the in-memory data tree.  Take care
             of other initialization tasks if full_init = True.
@@ -128,6 +130,12 @@
             file around after termination.  Default is False, to
             delete the temporary file.
 
+          dtd_schema: (optional): Only relevant if full_init=True.
+            When True, validate Manifest against DTD Schema file and
+            skip set_detaults() and semantic_validate().
+            Default is to validate against RelaxNG Schema file and
+            perform set_detaults() and semantic_validate().
+
         Raises:
           - TreeAccError exceptions during initialization of the project
             manifest data tree.
@@ -175,9 +183,12 @@
 
         # Schema and defval_manifest root names are taken from
         # valfile_base.
-        self.schema_name = valfile_base + ManifestServ.SCHEMA_SUFFIX
-        self.defval_manifest_name = valfile_base + \
-            ManifestServ.DEFVAL_XML_SUFFIX
+        if dtd_schema:
+            self.schema_name = valfile_base + ManifestServ.DTD_SCHEMA_SUFFIX
+        else:
+            self.schema_name = valfile_base + ManifestServ.SCHEMA_SUFFIX
+            self.defval_manifest_name = valfile_base + \
+                ManifestServ.DEFVAL_XML_SUFFIX
 
         # Create a new string without any prepended directories
         # from before the basename.  This will be used in creation
@@ -195,6 +206,7 @@
         self.out_manifest_name = out_manifest_name
         self.verbose = verbose
         self.keep_temp_files = keep_temp_files
+        self.dtd_schema = dtd_schema
 
         # Do this here in case cleanup() is called without
         # start_socket_server() having been called first.
@@ -205,21 +217,25 @@
 
         # Preprocess if full_init is specified.
         if (full_init):
-            self.set_defaults(self.defval_manifest_name,
-                              self.temp_manifest_name, self.verbose,
-                              self.keep_temp_files)
+            if not dtd_schema:
+                self.set_defaults(self.defval_manifest_name,
+                                  self.temp_manifest_name, self.verbose,
+                                  self.keep_temp_files)
+
             self.schema_validate(self.schema_name, self.temp_manifest_name,
                                  self.out_manifest_name, self.verbose,
-                                 self.keep_temp_files)
-            self.semantic_validate(self.defval_manifest_name,
-                                   self.temp_manifest_name, self.verbose,
-                                   self.keep_temp_files)
+                                 self.keep_temp_files, self.dtd_schema)
+
+            if not dtd_schema:
+                self.semantic_validate(self.defval_manifest_name,
+                                       self.temp_manifest_name, self.verbose,
+                                       self.keep_temp_files)
 
 
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     def schema_validate(self, schema_name=None, temp_manifest_name=None,
                         out_manifest_name=None, verbose=None,
-                        keep_temp_files=None):
+                        keep_temp_files=None, dtd_schema=None):
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         """ Validate manifest against the given schema
 
@@ -243,6 +259,11 @@
             If the value is not supplied here, the value specified in the
             constructor is used.
 
+          dtd_schema: boolean: True = validate against DTD Schema file
+            (and perform loading of attribute defaults from DTD).
+            False = validate against RelaxNG (and skip loading attribute
+            defaults).  If the value is not supplied here, the value
+            specified in the constructor is used.
         """
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         # Validate the project manifest data tree against its schema.
@@ -258,19 +279,43 @@
             verbose = self.verbose
         if (keep_temp_files is None):
             keep_temp_files = self.keep_temp_files
-	
+        if (dtd_schema is None):
+            dtd_schema = self.dtd_schema
+
+        delete_out_manifest = False
+        # In order to set attribute defaults (DTD only), there MUST be
+        # an output file from schema_validate().  So, if one wasn't
+        # specified, make up a temporary one (and delete it later).
+        if (dtd_schema == True) and (out_manifest_name is None):
+            out_manifest_name = temp_manifest_name.replace(
+                                    ManifestServ.XML_SUFFIX,
+                                    "_out" + ManifestServ.XML_SUFFIX)
+            delete_out_manifest = True
+
         # Pylint bug: See http://www.logilab.org/ticket/8764
         # pylint: disable-msg=C0321
         try:
             self.__save_tree(temp_manifest_name)
-            schema_validate(schema_name, temp_manifest_name, out_manifest_name)
+            schema_validate(schema_name, temp_manifest_name, out_manifest_name,
+                            dtd_schema=dtd_schema)
+
+            # For DTD Manifests, setting defaults entails taking the
+            # validation output file (out_manifest_name) and
+            # repopulating self.manifest_tree using this file.
+            if dtd_schema:
+                try:
+                    self.manifest_tree = TreeAcc(out_manifest_name)
+                except TreeAccError:
+                    print >> sys.stderr, "Error re-instantiating manifest tree:"
+                    raise
+
         except ManifestProcError, err:
             print >> sys.stderr, ("Error validating " +
                                   "manifest against schema " + schema_name)
             print >> sys.stderr, str(err)
             raise
 
-        # Check to delete the temporary file whether or not an
+        # Check to delete the temporary file(s) whether or not an
         # exception occurred.
         finally:
             if (not keep_temp_files):
@@ -278,6 +323,11 @@
                     print ("Removing temporary file: " + temp_manifest_name)
                 os.unlink(temp_manifest_name)
 
+                if delete_out_manifest:
+                    if verbose:
+                        print ("Removing temporary file: " + out_manifest_name)
+                    os.unlink(out_manifest_name)
+
 
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     def __load_defval_tree__(self, defval_manifest_name):
@@ -339,12 +389,17 @@
         Returns: None
 
         Raises:
+          ManifestServError: If self.dtd_schema is True
           KeyError: As raised from add_defaults()
           ManifestProcError: As raised from add_defaults()
 
         """
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+        if (self.dtd_schema):
+            raise ManifestServError, \
+			    ("set_defaults() called for DTD manifest")
+
         if (defval_manifest_name is None):
             defval_manifest_name = self.defval_manifest_name
         if (temp_manifest_name is None):
@@ -400,12 +455,17 @@
         Returns: None
 
         Raises:
+          ManifestServError: If self.dtd_schema is True
           KeyError: As raised from add_defaults()
           ManifestProcError: As raised from add_defaults()
 
         """
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+        if (self.dtd_schema):
+            raise ManifestServError, \
+			    ("semantic_validate() called for DTD manifest")
+
         if (defval_manifest_name is None):
             defval_manifest_name = self.defval_manifest_name
         if (temp_manifest_name is None):
--- a/usr/src/lib/install_utils/TreeAcc.py	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/lib/install_utils/TreeAcc.py	Fri Aug 20 11:31:18 2010 -0600
@@ -18,8 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 
 # =============================================================================
 # =============================================================================
@@ -1348,6 +1347,18 @@
         except IOError, err:
             raise FileOpenError, errno.errorcode[err.errno]
 
+        # If the original document contained a reference to an external
+        # DTD, then reconstruct the DOCTYPE field and write it out at
+        # the top of the output file.
+        # (This is essential for loading DTD attribute defaults later.)
+        if ((self.treedoc.doctype is not None) and
+            (self.treedoc.doctype.name is not None) and
+            (self.treedoc.doctype.systemId is not None) and
+            (self.treedoc.doctype.systemId.endswith(".dtd"))):
+            doctype_str = "<!DOCTYPE %s SYSTEM \"%s\">\n" % \
+                (self.treedoc.doctype.name, self.treedoc.doctype.systemId)
+            fp.write(doctype_str)
+
         # Write the data.
         # Pylint bug: See http://www.logilab.org/ticket/8764
         # pylint: disable-msg=C0321
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_utils/test/test_manifest_serv.py	Fri Aug 20 11:31:18 2010 -0600
@@ -0,0 +1,81 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+'''
+To run these tests, see the instructions in usr/src/tools/tests/README.
+Remember that since the proto area is used for the PYTHONPATH, the gate
+must be rebuilt for these tests to pick up any changes in the tested code.
+Note: the XML and DTD files are picked up from usr/src/cmd/auto-install
+and not the proto area.
+'''
+
+import gettext
+import unittest
+
+from osol_install.ManifestServ import ManifestServ
+from osol_install.DefValProc import ManifestProcError
+
+
+# This is the path for the Manifest and Schema files.
+# It is relative to usr/src in the current workspace.
+XML_DIR="cmd/auto-install"
+
+
+class DTDManifest(unittest.TestCase):
+    '''Tests for DTD Manifests'''
+
+    def setUp(self):
+        # Create a ManifestServ for the default Manifest, default.xml.
+        self.man_serv = ManifestServ("%s/default" % XML_DIR,
+            full_init=False)
+
+    def test_create(self):
+        '''ManifestServ: can be instantiated with manifest default.xml'''
+
+        instance_names = self.man_serv.get_values("auto_install/ai_instance/name")
+        print "instance_names = [%s]" % instance_names
+        self.assertEquals(len(instance_names), 1)
+        self.assertEquals(instance_names[0], "default")
+
+    def test_validate_fails_if_dtd_schema_is_false(self):
+        '''ManifestServ: validate fails if dtd_schema is False'''
+
+        self.assertRaises(ManifestProcError,
+            self.man_serv.schema_validate,
+            schema_name="%s/ai.dtd" % XML_DIR,
+            dtd_schema=False)
+
+    def test_validate_succeeds_if_dtd_schema_is_true(self):
+        '''ManifestServ: validate succeeds if dtd_schema is True'''
+
+        try:
+            self.man_serv.schema_validate(
+                schema_name="%s/ai.dtd" % XML_DIR,
+                dtd_schema=True)
+        except ManifestProcError, err:
+            self.fail("schema_validate unexpectedly failed: [%s]" % str(err))
+
+
+if __name__ == '__main__':
+    unittest.main()
--- a/usr/src/lib/libict/ict.c	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/lib/libict/ict.c	Fri Aug 20 11:31:18 2010 -0600
@@ -793,7 +793,7 @@
 	    "/var/svc/log/application-auto-installer:default.log",
 	    "/var/svc/log/application-manifest-locator:default.log",
 	    "/var/adm/messages",
-	    "/tmp/ai_manifest.xml",
+	    "/tmp/ai.xml",
 	    NULL };
 	boolean_t	redirect = B_FALSE;
 
--- a/usr/src/man/installadm.1m.txt	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/man/installadm.1m.txt	Fri Aug 20 11:31:18 2010 -0600
@@ -1,26 +1,3 @@
-'\" te
-.\" 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 2010 Sun Microsystems, Inc.  All rights reserved.
-.\" Use is subject to license terms.
-.\"
 System Administration Commands               installadm(1M)        
 
 NAME
@@ -43,9 +20,13 @@
 
      installadm disable <svcname>
 
-     installadm add -m <manifest> -n <svcname>
+     installadm add-manifest -m <manifest> -n <svcname>
+     [-c <criteria=value|range> ... | -C <criteria.xml>]
 
-     installadm remove -m <manifest> -n <svcname>
+     installadm delete-manifest -m <manifest> -n <svcname>
+
+     installadm set-criteria -m <manifest> -n <svcname>
+     -a|-c <criteria=value|range> ... | -C <criteria.xml>
 
      installadm create-client [-b <property>=<value>,...] 
      [-t <imagepath>] -e <macaddr> -n <svcname> 
@@ -111,8 +92,8 @@
      specifications, you can manually create or modify 
      manifests so that the manifests cover those specific 
      machine specifications. Then, you can use the 
-     installadm add command to add your new manifests to an 
-     install service.
+     installadm add-manifest command to add your new manifests
+     to an install service.
 
      If you want a specific client to use a specific 
      install service, you can associate a service to a 
@@ -124,6 +105,7 @@
      - Set up installation images
      - Set up or remove clients
      - Add or delete manifests
+     - Specify or modify criteria for a manifest
      - Enable or disable install services
      - List install services
      - List manifests for an install service
@@ -285,12 +267,16 @@
 
 
     
-     installadm add -m <manifest> -n <svcname>
+     installadm add-manifest -m <manifest> -n <svcname>
+     [-c <criteria=value|range> ... | -C <criteria.xml>]
 
           Associates manifests with a specific install 
           service, thus making the manifests available on 
           the network, independently from creating a 
-          service.
+          service. When publishing a non-default manifest,
+          it is required to associate criteria either via
+          criteria entered on the command line (-c) or 
+          via a criteria XML file (-C).
 
      -m   <manifest> 
           Required: Specifies the path name to a criteria 
@@ -312,9 +298,25 @@
           service name will be "_install_service_46510." 
 
     
-     installadm remove -m <manifest> -n <svcname>
+     -c   <-c <criteria=value|range> ...> 
+          Optional: Specifies criteria to be associated with the
+          added non-default manifest. When publishing a default
+          manifest, criteria must not be specified. When 
+          publishing a non-default manifest, criteria must be
+          specified.
 
-          Remove a manifest that was published with a 
+     -C   <criteria.xml> 
+          Optional: Specifies the path name of a criteria 
+          XML file containing criteria to be associated with the
+          added non-default manifest. When publishing a default
+          manifest, criteria must not be specified. When 
+          publishing a non-default manifest, criteria must be 
+          specified.
+
+
+     installadm delete-manifest -m <manifest> -n <svcname>
+
+          Deletes a manifest that was published with a 
           specific install service.
 
      -m   <manifest> 
@@ -326,6 +328,36 @@
           service this manifest is associated with.
 
     
+     installadm set-criteria -m <manifest> -n <svcname>
+     -a|-c <criteria=value|range> ... | -C <criteria.xml>
+
+          Updates criteria of an already published manifest.
+          Criteria can be specified via the command line or
+          or via a criteria xml file. Criteria must be 
+          specified with one of the mutually exclusive
+          options, -a, -c, or -C.
+
+     -m   <manifest> 
+          Required: Specifies the name of a manifest.
+
+     -n   <svcname>
+          Required: Specifies the name of the install 
+          service this manifest is associated with.
+
+     -c   <-c <criteria=value|range> ...> 
+          Optional: Specifies criteria to replace all existing
+          criteria for the manifest.
+
+     -a   <-a <criteria=value|range> ...> 
+          Optional: Specifies criteria to be appended to the
+          existing criteria for the manifest. If the criteria 
+          specified already exists, the value/range of that
+          criteria is replaced by the specified value/range.
+
+     -C   <criteria.xml> 
+          Optional: Specifies the path name of a criteria 
+          XML file containing criteria to replace all existing 
+          criteria for the manifest.
 
      installadm create-client [-b <property>=<value>,...] 
      [-t <imagepath>] -e <macaddr> -n <svcname> 
@@ -380,9 +412,43 @@
           If subcommand is provided, the command provides
           the syntax for that subcommand.
 
+CRITERIA FILES
 
+     A criteria XML file allows you to specify criteria for a
+     manifest by passing the file to the add-manifest or 
+     set-criteria commands. Criteria can be specified as a
+     value or a range, by using the following tags.
+
+     For a criterion with a specific value:
+
+     <ai_criteria_manifest>
+         <ai_criteria name=XXXX>
+             <value>yyyy</value>
+         </ai_criteria>
+     </ai_criteria_manifest>
+
+     where XXXX is the name of the criterion (e.g. MAC, IPV4,
+     MEM, or ARCH) and yyyy is the value of the criterion.
 
+     For a criterion with a range:
 
+     <ai_criteria_manifest>
+         <ai_criteria name=XXXX>
+             <range>
+                 yyyy1
+                 yyyy2
+             </range>
+         </ai_criteria>
+     </ai_criteria_manifest>
+
+     where XXXX is the name of the criterion (e.g. MAC, IPV4,
+     or MEM) and yyyy1 and yyyy2 are the lower and uppper 
+     bounds of the range.
+
+     Multiple criteria may be specified in the file between
+     the <ai_criteria_manifest> and </ai_criteria_manifest>
+     tags.
+ 
 EXAMPLES
 
 
@@ -507,7 +573,7 @@
      to add a new custom manifest to an existing install 
      service: 
  
-     # installadm add -m criteria_mac.xml \
+     # installadm add-manifest -m criteria_mac.xml \
      -n service_032509
 
      The command assigns the manifest, criteria_mac.xml
@@ -575,13 +641,51 @@
 .in -9
 .sp
 
+     Example 7: Use the following sample command to add manifest1 
+     to svc1 with a criteria of MAC address equaling
+     "aa:bb:cc:dd:ee:ff":
+
+         # installadm add-manifest -m manifest1 -n svc1 
+           -c MAC="aa:bb:cc:dd:ee:ff" 
+
+     Example 8: Use the following sample command to add manifest2 
+     to svc1 with a criteria of an IPv4 range between 10.0.2.100
+     to 10.0.2.199:
+
+         # installadm add-manifest -m manifest2 -n svc1 
+           -c IPV4="10.0.2.100-10.0.2.199" 
+
+     Example 9: Use the following sample command to add manifest3 
+     to svc1 with a criteria of 2048MB memory or greater and an
+     architecture of i86pc:
+
+         # installadm add-manifest -m manifest3 -n svc1
+           -c MEM="2048-unbounded" -c ARCH=i86pc
+     
+     Example 10: Use the following sample command to append to
+     the criteria of manifest2 of svc1, a criterion of 4096MB
+     memory or greater:
+
+         # installadm set-criteria -m manifest2 -n svc1
+           -a MEM="4096-unbounded"
+     
+     Example 11: Use the following sample command to replace the
+     criteria of manifest2 of svc1 with the criteria specified by 
+     the file, /tmp/criteria.xml:
+
+         # installadm set-criteria -m manifest2 -n svc1
+           -C /tmp/criteria.xml
+    
+     See the CRITERIA FILES section for more information on the
+     contents of the criteria xml file.
+
 ATTRIBUTES
      See attributes(5) for descriptions of the  following  attri-
      butes:
      ____________________________________________________________
     |       ATTRIBUTE TYPE        |       ATTRIBUTE VALUE       |
     |_____________________________|_____________________________|
-    | Availability                | SUNWinstalladm-tools        |
+    | Availability                | install/installadm          |
     |_____________________________|_____________________________|
     | Interface Stability         | None / Under Development    |
     |_____________________________|_____________________________|
@@ -600,5 +704,5 @@
      getstart/
 
 
-		Last Changed March 4, 2010
+		Last Changed August 17, 2010
 
--- a/usr/src/pkg/manifests/install-installadm.mf	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/pkg/manifests/install-installadm.mf	Fri Aug 20 11:31:18 2010 -0600
@@ -82,7 +82,8 @@
 file path=usr/lib/installadm/delete-manifest
 file path=usr/lib/installadm/installadm-common
 file path=usr/lib/installadm/list
-file path=usr/lib/installadm/publish-manifest
+file path=usr/lib/installadm/publish_manifest.py
+file path=usr/lib/installadm/set_criteria.py
 file path=usr/lib/installadm/setup-dhcp
 file path=usr/lib/installadm/setup-image
 file path=usr/lib/installadm/setup-service
--- a/usr/src/pkg/manifests/system-install-auto-install-auto-install-common.mf	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/pkg/manifests/system-install-auto-install-auto-install-common.mf	Fri Aug 20 11:31:18 2010 -0600
@@ -40,7 +40,10 @@
 dir path=usr/share/auto_install/sc_profiles group=sys
 file path=usr/lib/python2.6/vendor-packages/osol_install/auto_install/__init__.py mode=0444
 file path=usr/lib/python2.6/vendor-packages/osol_install/auto_install/__init__.pyc mode=0444
-file path=usr/share/auto_install/ai_manifest.rng mode=0444 group=sys
+file path=usr/share/auto_install/ai.dtd mode=0444 group=sys
+file path=usr/share/auto_install/target.dtd mode=0444 group=sys
+file path=usr/share/auto_install/configuration.dtd mode=0444 group=sys
+file path=usr/share/auto_install/software.dtd mode=0444 group=sys
 file path=usr/share/auto_install/ai_manifest.xml mode=0444 group=sys
 file path=usr/share/auto_install/default.xml mode=0444 group=sys
 file path=usr/share/auto_install/sc_profiles/static_network.xml mode=0444 group=sys
--- a/usr/src/pkg/manifests/system-install-auto-install.mf	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/pkg/manifests/system-install-auto-install.mf	Fri Aug 20 11:31:18 2010 -0600
@@ -53,7 +53,6 @@
 file path=usr/lib/python2.6/vendor-packages/osol_install/auto_install/ai_parse_manifest.py mode=0444
 file path=usr/lib/python2.6/vendor-packages/osol_install/auto_install/ai_parse_manifest.pyc mode=0444
 file path=usr/sbin/get_manifest mode=0555
-file path=usr/share/auto_install/ai_manifest.defval.xml group=sys
 file path=lib/svc/manifest/application/auto-installer.xml mode=0444 group=sys
 file path=lib/svc/manifest/application/manifest-locator.xml mode=0444 group=sys
 license cr_Sun license=cr_Sun
--- a/usr/src/tools/tests/README	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/tools/tests/README	Fri Aug 20 11:31:18 2010 -0600
@@ -67,7 +67,16 @@
 portion, and supply the path to the file or package, relative to the usr/src
 directory of the workspace. For example, the following command runs only
 the liberrsvc_pymod tests:
-	./slim_test lib/liberrsvc_pymod/tests 
+	./slim_test lib/liberrsvc_pymod/test 
+
+To run all tests in a specific file:
+	./slim_test lib/liberrsvc_pymod/test/test_liberrsvc.py
+
+To run a specific class of tests in that file:
+	./slim_test lib/liberrsvc_pymod/test/test_liberrsvc.py:error_service
+
+To run a particular test in that class:
+	./slim_test lib/liberrsvc_pymod/test/test_liberrsvc.py:error_service.test_create_error_info
 
 To report code coverage metrics along with test results, add a "--with-cover"
 flag:
--- a/usr/src/tools/tests/config.nose	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/tools/tests/config.nose	Fri Aug 20 11:31:18 2010 -0600
@@ -27,4 +27,4 @@
 verbosity=2
 with-doctest=1
 doctest-result-variable=__
-cover-package=osol_install
+cover-package=osol_install,publish_manifest,set_criteria
--- a/usr/src/tools/tests/slim_test	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/tools/tests/slim_test	Fri Aug 20 11:31:18 2010 -0600
@@ -29,6 +29,7 @@
 	exit 3
 fi
 
-export PYTHONPATH=${ROOT}/usr/snadm/lib:${ROOT}/usr/lib/python2.6/vendor-packages/
+export PYTHONPATH=${ROOT}/usr/snadm/lib:${ROOT}/usr/lib/python2.6/vendor-packages/:\
+${ROOT}/usr/lib/installadm
 
 nosetests -c ${SRC}/tools/tests/config.nose -w $SRC "$@"
--- a/usr/src/tools/tests/tests.nose	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/tools/tests/tests.nose	Fri Aug 20 11:31:18 2010 -0600
@@ -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/text-install/osol_install/text_install/test/,cmd/installadm/installadm_common.py
+tests=lib/liberrsvc_pymod/test/,cmd/ai-webserver/test/,cmd/text-install/osol_install/text_install/test/,cmd/installadm/installadm_common.py,lib/install_utils/test/