--- a/usr/src/cmd/aimanifest/aimanifest.py Fri Aug 05 08:42:53 2011 -0600
+++ b/usr/src/cmd/aimanifest/aimanifest.py Fri Aug 05 12:08:36 2011 -0700
@@ -46,7 +46,6 @@
fallback=True).gettext
AIM_LOGGER = None
-SCHEMA_FILE = "/usr/share/install/ai.dtd"
VALIDATE = True
NO_VALIDATE = False
@@ -89,7 +88,14 @@
"the returned element\n" +
" in terms of node IDs. This path may be used in " +
"subsequent calls to\n" +
- " %s to specify the affected element more directly.\n") % (
+ " %s to specify the affected element more directly.\n" +
+ "\n The following environment variables are read:\n" +
+ " AIM_MANIFEST: Pathname of the evolving manifest. " +
+ "(Must be set)\n" +
+ " AIM_DTD: Overrides the DTD given in the evolving manifest. " +
+ "(Optional)\n" +
+ " AIM_LOGFILE: Logfile for additional information. " +
+ "(Optional)\n") % (
name, name)
return usage_str
@@ -224,7 +230,8 @@
# Pass AIM_MANIFEST as the output file.
try:
- mim = ManifestInput(os.environ.get("AIM_MANIFEST"), SCHEMA_FILE)
+ mim = ManifestInput(os.environ.get("AIM_MANIFEST"),
+ os.environ.get("AIM_DTD"))
except (milib.MimEtreeParseError, milib.MimDTDInvalid) as err:
for error in err.errors:
# These messages come already localized.
@@ -281,7 +288,7 @@
try:
mim.load(path, options.is_incremental)
mim.commit(NO_VALIDATE)
- except milib.MimEtreeParseError as err:
+ except (milib.MimEtreeParseError, milib.MimDTDInvalid) as err:
for error in err.errors:
# These messages come already localized.
print >> sys.stderr, error
@@ -297,7 +304,7 @@
try:
mim.validate()
AIM_LOGGER.info(_("Validation successful"))
- except milib.MimDTDInvalid as err:
+ except (milib.MimEtreeParseError, milib.MimDTDInvalid) as err:
errorlist = err.errors
for error in errorlist:
# These messages come already localized.
--- a/usr/src/lib/install_manifest_input/__init__.py Fri Aug 05 08:42:53 2011 -0600
+++ b/usr/src/lib/install_manifest_input/__init__.py Fri Aug 05 12:08:36 2011 -0700
@@ -146,9 +146,9 @@
ERR_AMBIG_PATH = _("Ambiguity error: Path matches more than one element")
ERR_AMBIG_PARENT_PATH = _("Ambiguity error: "
"Parent path matches more than one element")
-ERR_NO_PARENT_PATH = _("Error: no matching parent path exists")
-ERR_NO_ELEM_MATCH = _("Error: Path matches no elements")
-ERR_NO_ATTR_MATCH = _("Error: Path matches no attributes")
+ERR_NO_PARENT_PATH = _("No matching parent path exists")
+ERR_NO_ELEM_MATCH = _("Path matches no elements")
+ERR_NO_ATTR_MATCH = _("Path matches no attributes")
ERR_FINAL_BRANCH_VAL_INVALID = _("Final path branch has a value or is invalid")
ERR_FINAL_BRANCH_INVALID = _("Final path branch is invalid")
ERR_ETREE_PARSE_XPATH = _("Etree error parsing path %(mxpath)s: %(merr)s")
--- a/usr/src/lib/install_manifest_input/mim.py Fri Aug 05 08:42:53 2011 -0600
+++ b/usr/src/lib/install_manifest_input/mim.py Fri Aug 05 12:08:36 2011 -0700
@@ -54,7 +54,6 @@
import re
from lxml import etree
-from urllib2 import urlopen
import solaris_install.manifest_input as milib
import solaris_install.manifest_input.process_dtd as pdtd
@@ -133,10 +132,8 @@
evolving_file: Pathname to the file providing initial data, and
storing the result.
- schema_file: Pathname to the DTD file used. Used only if the
- manifest does not name a DTD. Optional, but if not specified
- here and manifest does not specify either, then an exception
- is raised.
+ schema_file: Pathname to the DTD file to use. Overrides any DTD
+ specified in the manifest, if specified. Optional.
Raises:
IOError - Could not access DTD file
@@ -154,6 +151,11 @@
self.evfile_name = evolving_file
self.tree = None
+ self.schema = None
+ self.schema_data = None
+ self.schema_file_from_mfest = None
+ self.schema_file_from_init = None
+ self.schema_file_from_overlay = None
# Set up parser to remove blank text and processing instructions.
# Have parser leave in comments, so they can be written out later.
@@ -174,15 +176,35 @@
if mfest_size:
self.tree = self.parse_xml_file(self.evfile_name, self.parser)
+ # Save explicit requested schema. Could be None.
+ self.schema_file_from_init = schema_file
+
+ if self.tree and self.tree.docinfo:
+ self.schema_file_from_mfest = self.tree.docinfo.system_url
+ if schema_file is None:
+ schema_file = self.tree.docinfo.system_url
+
+ # Load schema. Forgo setting the schema now if there is no place to
+ # get it from. Try to get it from the loaded manifest later.
+ if schema_file is not None:
+ self.load_schema(schema_file)
+
+ def load_schema(self, schema_file):
+ '''
# Open schema for validator, and build table of children order.
- # Use the schema refered to in the manifest itself, if it is listed in
- # the manifest and it exists. Else use the schema passed in as an arg.
- if (self.tree and self.tree.docinfo and
- self.tree.docinfo.system_url and
- ManifestInput._is_accessible(self.tree.docinfo.system_url)):
- schema_file = self.tree.docinfo.system_url
+ Args:
+ schema_file: DTD
+
+ Returns:
+ initializes self.schema and self.schema_data
+ Raises:
+ IOError - Could not access DTD file.
+ IOError - Could not digest DTD file.
+ MimDTDInvalid - Error parsing DTD
+ MimDTDError - SchemaData error processing DTD data from file.
+ '''
if schema_file is None:
raise milib.MimInvalidError(milib.ERR_NO_SCHEMA)
@@ -198,7 +220,8 @@
err.error_log.filter_from_level(GET_ALL)])
try:
- self.schema_data = pdtd.SchemaData(schema_file) # For order table
+ # For order table
+ self.schema_data = pdtd.SchemaData(schema_file)
except IOError as err:
raise IOError(err.args[0], milib.IOERR_DTD_DIGEST %
{"mserr": err.strerror, "mfile": schema_file})
@@ -207,33 +230,6 @@
{"mfile": schema_file, "merr": str(err)})
@staticmethod
- def _is_accessible(url):
- '''
- Return true if url is accessible.
-
- Checks for local files as well as proper URLs.
-
- Args:
- url: A string that is either a URL or a local filename.
-
- Returns:
- True: The url is accessible.
- False: The url is not accessible.
- '''
- if os.access(url, os.R_OK):
- return True
-
- try:
- # Send no additional data to the server.
- # Allow up to 5 seconds for connection to be made.
- fd = urlopen(url, data=None, timeout=5)
- fd.close()
- except IOError:
- return False
-
- return True
-
- @staticmethod
def parse_xml_file(manifest_name, parser):
'''
Call etree.parse with proper exception handling.
@@ -252,7 +248,7 @@
'''
try:
tree = etree.parse(manifest_name, parser)
- except etree.XMLSyntaxError as err:
+ except etree.XMLSyntaxError:
raise milib.MimEtreeParseError(
[msg.__repr__() for msg in
parser.error_log.filter_from_level(GET_ALL)])
@@ -273,6 +269,7 @@
IOError - Error reading overlay_filename
MimInvalidError - Argument is missing or invalid
MimEtreeParseError - IO errors or parser errors while parsing.
+ MimDTDInvalid - Error reading DTD
'''
if overlay_filename is None:
raise milib.MimInvalidError(milib.ERR_ARG_INVALID)
@@ -280,9 +277,27 @@
if (not incremental) or not self.tree:
# Load a fresh tree. Discard old data.
self.tree = self.parse_xml_file(overlay_filename, self.parser)
+
+ # Take the schema from the manifest being loaded.
+ # Record that tree stores a schema.
+ if self.tree and self.tree.docinfo:
+ self.schema_file_from_mfest = self.tree.docinfo.system_url
+
+ # Allow for no schemas from anywhere for fresh trees since
+ # no merges are needed.
+ if (self.tree.docinfo.system_url is not None and
+ not self.schema):
+ self.load_schema(self.tree.docinfo.system_url)
else:
# Read tree of overlay data, then overlay it.
overlay_tree = self.parse_xml_file(overlay_filename, self.parser)
+
+ # No schema loaded. Take the schema from the overlay tree.
+ # Overlay tree schema doesn't get stored in tree.
+ if overlay_tree and overlay_tree.docinfo and not self.schema:
+ self.load_schema(overlay_tree.docinfo.system_url)
+ self.schema_file_from_overlay = overlay_tree.docinfo.system_url
+
self._overlay_recurse(self.tree.getroot(), overlay_tree.getroot(),
None)
@@ -319,6 +334,8 @@
MimEmptyTreeError - No XML data present
Various lxml.etree (StandardError subclass) exceptions
'''
+ if not self.schema:
+ raise milib.MimDTDInvalid([milib.ERR_NO_SCHEMA])
if not self.tree:
raise milib.MimEmptyTreeError(milib.ERR_EMPTY_TREE)
try:
@@ -349,8 +366,31 @@
raise milib.MimEmptyTreeError(milib.ERR_EMPTY_TREE)
if validate:
self.validate()
+ xml_data = etree.tostring(self.tree, pretty_print=True).splitlines(
+ True)
+ # DOCTYPE string will have one of the following, in order:
+ # 1) User specified DTD (self.schema_file_from_init)
+ # 2) DTD from loaded tree (self.schema_file_from_mfest)
+ # 3) DTD from overlaid tree (self.schema_file_from_overlay)
+
+ if self.schema_file_from_init:
+ # Store as doctype the explicit schema requested by the user.
+ if self.schema_file_from_mfest:
+ # Replace the DOCTYPE string, always the first line.
+ xml_data[0] = '<!DOCTYPE %s SYSTEM "%s">\n' % (
+ self.tree.getroot().tag, self.schema_file_from_init)
+ else:
+ # No doctype is stored in the tree. Just add requested one.
+ xml_data.insert(0, '<!DOCTYPE %s SYSTEM "%s">\n' % (
+ self.tree.getroot().tag, self.schema_file_from_init))
+ elif self.schema_file_from_overlay and not self.schema_file_from_mfest:
+ # No doctype is stored in the tree. Just add requested one.
+ xml_data.insert(0, '<!DOCTYPE %s SYSTEM "%s">\n' % (
+ self.tree.getroot().tag, self.schema_file_from_overlay))
+
try:
- self.tree.write(self.evfile_name, pretty_print=True)
+ with open(self.evfile_name, "w") as outfile:
+ outfile.writelines(xml_data)
except IOError as err:
raise IOError(milib.IOERR_DTD_DEST %
{"mserr": err.strerror, "mdest": self.evfile_name})
@@ -508,10 +548,10 @@
does not exist), a new node is created. Otherwise, an existing one
is followed.
- * A simple branch is simply an idenfier. /a/b are two simple branches.
- A non-simple branch is any other kind of branch, such as /a=5 or
- /b[c/d@e=123]. Non-simple branches always specify a value and may
- specify a subpath.
+ * A simple branch is simply an identifier. /a/b are two simple
+ branches. A non-simple branch is any other kind of branch, such as
+ /a=5 or /b[c/d@e=123]. Non-simple branches always specify a value and
+ may specify a subpath.
The goal here is to honor subpaths which may be specified to narrow
down where to add new items, but to still create a second node of a
@@ -617,8 +657,14 @@
for branch in right_set:
# insert_before holds a list of names which can follow
# an element named "branch" as children of curr_elem.
- (insert_before, mults_ok) = \
- self.schema_data.find_element_info(curr_elem.tag, branch)
+ #
+ if self.schema_data:
+ (insert_before, mults_ok) = \
+ self.schema_data.find_element_info(curr_elem.tag, branch)
+ else:
+ # No schema to go on here. Just allow the insert.
+ insert_before = []
+ mults_ok = True
# Check for no list (as oppoosed to empty list)
if insert_before == None:
@@ -812,6 +858,8 @@
- If the overlay_element is a leaf node, then replace the node in
the main tree with the overlay_element.
+ Assumes self.schema_data is initialized.
+
Args:
main_parent: Parent of where new element would be added.