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
--- 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/