예제 #1
0
    def manifest(self):
        '''
            Instance accessor for the manifest to be parsed

            The use of a property here is to ensure _manifest has a valid
            value, either passed in as an argument to __init__() or via
            reading the DOC where the location to the manifest to be parsed
            is stored by another consumer.

            If manifest to be parsed is passed in as an __init__() argument
            then self._manifest will already be set, and there's not need
            to read the DOC. Constructor argument takes precedence over DOC.

            Raise ManifestError exception if manifest is not available or
            manifest file does not exist.
        '''
        if self._manifest is None:
            self._manifest = self.get_manifest_from_doc()

        if self._manifest is None:
            raise ManifestError("No manifest specified")

        if not os.path.isfile(self._manifest):
            raise ManifestError("Manifest [%s] is not a file" % \
                (self._manifest))

        return self._manifest
예제 #2
0
    def write(self, doc):
        '''
            This API method is not part of the AbstractCheckpoint spec.
            It can be used to access the ManifestParser functionality outside
            the InstallEngine context.

            This method is also used as a convenience function within this
            class to do most of the work of the execute() method.

            Parameters:
            - doc, a reference to the DataObjectCache instance from which to
              export the manifest data.

            Returns:
            - Nothing
              On success, this method returns; on error it raises an exception.

            Raises:
            ManifestError is raised if:
            - xslt_file cannot be read or is not a valid XSLT file
            - output file cannot be created or written to
            or if validate_manifest raises an error.
        '''

        self.logger.debug("ManifestWriter.write(doc=%s) called", doc)

        if self._cancel_requested.is_set():
            self.logger.debug("Cancel requested, returning.")
            return

        # Get XML data from DOC.  This always returns something
        xml = doc.generate_xml_manifest()

        tree = etree.ElementTree(xml)
        if self.logger.isEnabledFor(logging.DEBUG):
            self.logger.debug("XML returned from DOC:\n%s\n",
                              etree.tostring(tree, pretty_print=True))

        if self._xslt_file is not None:
            # Perform the requested XSL Transform on the XML data
            try:
                xslt_doc = etree.parse(self._xslt_file)
            except IOError, error:
                msg = "Cannot access XSLT file [%s]" % (self._xslt_file)
                self.logger.exception(msg)
                self.logger.exception(error)
                raise ManifestError(msg, orig_exception=error)
            except etree.XMLSyntaxError, error:
                msg = "XML syntax error in XSLT file [%s]" % \
                    (self._xslt_file)
                self.logger.exception(msg)
                self.logger.exception(error)
                raise ManifestError(msg, orig_exception=error)
예제 #3
0
    def execute(self, dry_run=False):
        '''
            Abstract method defined in AbstractCheckpoint class.

            Exports data from InstallEngine's DataObjectCache to the
            file named in self._manifest.

            Parameters:
            - dry_run is used to control what actions are taken if
              self._manifest already exists.  If dry_run is False, the
              file will be overwritten if it exists.  if dry_run is
              True, the output will be written to a similarly-named,
              but non-existing file.

            Returns:
            - Nothing
              On success, this method returns; on error it raises an exception.

            Raises:
            - ManifestError is raised if unable to fetch DOC reference or
              if an error occurs in write().
        '''

        self.logger.debug("ManifestWriter.execute(dry_run=%s) called", dry_run)

        engine = InstallEngine.get_instance()

        doc = engine.data_object_cache
        if doc is None:
            raise ManifestError("Cannot get DOC reference from InstallEngine")

        if dry_run and os.path.exists(self._manifest):
            self._manifest = _create_unique_variant(self._manifest)

        self.write(doc)
예제 #4
0
    def _load_manifest(self, dtd_validation=False, attribute_defaults=True):
        '''
            Loads the manifest contained in property self.manifest.

            Parameters:
            - dtd_validation must be True or False.  Default is False.  If
              True, then the document will also be validated on-the-fly as
              it is loaded.
            - attribute_defaults must be True or False.  Default is True.
              Only relevant if the manifest references a DTD.  If True, then
              default values for XML attributes given in the DTD will be
              loaded as the document is parsed.

            Returns:
            - an etree.ElementTree object

            Raises:
            - ManifestError is raised if the manifest file cannot be
              accessed or if XMLSyntaxError is raised while parsing
              and, optionally, validating it.
        '''

        # Create the XML parser to be used when processing the manifest.
        parser = etree.XMLParser(remove_blank_text=True,
            dtd_validation=dtd_validation,
            attribute_defaults=attribute_defaults)

        try:
            tree = etree.parse(self.manifest, parser)
        except IOError, error:
            msg = "Cannot access Manifest file [%s]" % (self.manifest)
            self.logger.exception(msg)
            self.logger.exception(error)
            raise ManifestError(msg, orig_exception=error)
예제 #5
0
    def execute(self, dry_run=False):
        '''
            Abstract method defined in AbstractCheckpoint class.

            Loads the specified Manifest and does the requested validation.
            Imports resulting data into DOC.

            Parameters:
            - the dry_run keyword paramater, specified in AbstractCheckpoint,
              is ignored in this method.

            Returns:
            - Nothing
              On success, this method returns; on error it raises an exception.

            Raises:
            - ManifestError is raised if unable to fetch DOC reference or
              if an error occurs in parse().
        '''

        self.logger.debug("ManifestParser.execute(dry_run=%s) called", dry_run)

        engine = InstallEngine.get_instance()

        doc = engine.data_object_cache
        if doc is None:
            raise ManifestError("Cannot get DOC reference from InstallEngine")

        self.parse(doc=doc)
예제 #6
0
def _create_unique_variant(orig_filename):
    '''
        Create a variant of the passed-in filename, which does not already
        exist.  This uses the tempfile module to create a file whose name
        is based on orig_filename but with some random letters and numbers
        inserted.
    '''

    dirname, filename = os.path.split(orig_filename)
    prefix, suffix = os.path.splitext(filename)

    file_desc, new_filename = \
        tempfile.mkstemp(suffix=suffix, prefix=prefix + "_", dir=dirname)

    try:
        os.close(file_desc)
    except IOError:
        raise ManifestError("Could not close temp file [%s]" % new_filename)

    return new_filename
예제 #7
0
    def __init__(self,
                 name,
                 manifest,
                 xslt_file=None,
                 validate_from_docinfo=False,
                 dtd_file=None):
        '''
            Class initializer method.

            Parameters:
            - name arg is required by AbstractCheckpoint. Not used.
            - manifest must be the path to a writable XML file
            - xslt_file defaults to None.  Otherwise, it must be the
              path to a readable XSLT file which will be applied to
              the manifest XML data to transform it into the required
              format.  Typically, this is used to structure the data
              according to the AI or DC schema.
            - validate_from_docinfo defaults to False.  It is only
              relevant if, following XSL Transformation, the XML document
              contains a reference to a DTD.  This will typically have
              been added by the XSLT file.  If validate_from_docinfo is
              True, the XML document will be validated against its
              referenced DTD, if present.  If validation fails, an
              error is raised.  If validate_from_docinfo is False or
              no DTD is referenced, this validation is skipped.
            - dtd_file defaults to None.  Otherwise, it must be the
              path to a DTD file against which the manifest XML will be
              validated before it is written out.  If validation is
              attempted and fails, an error is raised.  This validation
              is separate from that controlled by validate_from_docinfo.

            Returns:
            - Nothing

            Raises:
            - ManifestError is raised if invalid values are specified
              for any paramaters or if xslt_file or dtd_file are specified
              but do not exist.
        '''

        super(ManifestWriter, self).__init__(name)

        self.logger.debug(
            "Initializing ManifestWriter "
            "(manifest=%s, xslt_file=%s, "
            "validate_from_docinfo=%s, dtd_file=%s)", manifest, xslt_file,
            validate_from_docinfo, dtd_file)

        # Check and store params

        self._manifest = manifest

        if ((xslt_file is not None) and (not os.path.isfile(xslt_file))):
            raise ManifestError("XSLT file [%s] is not a file" % xslt_file)
        self._xslt_file = xslt_file

        self._validate_from_docinfo = validate_from_docinfo

        if ((dtd_file is not None) and (not os.path.isfile(dtd_file))):
            raise ManifestError("DTD [%s] is not a file" % dtd_file)
        self._dtd_file = dtd_file
예제 #8
0
        if self._cancel_requested.is_set():
            self.logger.debug("Cancel requested, returning.")
            return

        self.logger.debug("About to write out:\n%s\n", text)

        # Write to output file
        manifest_file = None
        if not os.path.exists(os.path.dirname(self._manifest)):
            try:
                os.makedirs(os.path.dirname(self._manifest))
            except IOError, error:
                msg = 'Cannot create directory for Manifest [%s]' % \
                        self._manifest
                raise ManifestError(msg, orig_exception=error)
        try:
            manifest_file = open(self._manifest, mode='w')
            manifest_file.write(text)
        except IOError, error:
            msg = "Cannot write to output Manifest [%s]" % (self._manifest)
            self.logger.exception(msg)
            raise ManifestError(msg, orig_exception=error)
        finally:
            if manifest_file is not None:
                manifest_file.close()

    def execute(self, dry_run=False):
        '''
            Abstract method defined in AbstractCheckpoint class.
예제 #9
0
    def __init__(self, name, manifest=None, validate_from_docinfo=None,
        dtd_file=None, load_defaults=True, call_xinclude=False):
        '''
            Class initializer method.

            Parameters:
            - name arg is required by AbstractCheckpoint.  Not used.
            - manifest, if passed,  must be the path to a readable XML file.
              The manifest value can be set when the object is instantiated
              by passing in a value for this param, or setting can be deferred
              until later by not passing in any value here. In this case,
              before the 1st attempt to access the manifest, there must
              be a valid manifest value stored in the DataObjectCache at the
              location specified by MANIFEST_PARSER_DATA.
            - validate_from_docinfo controls whether the manifest is
              validated against the DTD in the file's XML headers, if any.
              The default value is None.  This parameter can have 3 values:
              None: validate against the DTD, if present; if DTD URI is not
                present, no error is raised
              True: attempt to validate against the DTD on-the-fly as it is
                loaded.  If DTD is not specified, raise an error
              False: whether or not a DTD is specified, do not attempt to
                validate against it
              If validation is attempted and fails, an error is raised.
            - dtd_file specifes the path to a DTD against which the manifest
              will be validated.  This validation may be performed instead
              of, or as well as, the validation controlled by
              validate_from_docinfo, or it may be skipped by leaving dtd_file
              as None (the default).  If validation is attempted and fails,
              an error is raised.
              Note: default attribute values (see below) cannot be loaded
              during this form of validation - they can only be loaded if
              the manifest directly references a DTD in its headers.
            - load_defaults must be either True or False.  The default value
              is True.  load_defaults is only relevant when the manifest
              references a DTD.  If True, default attribute values from the
              DTD are loaded when the manifest is parsed.  If the manifest
              does not reference a DTD, no defaults are loaded and no error
              is raised.  (Note: Defaults can be loaded even if
              validate_from_docinfo is False.)
            - call_xinclude must be either True or False and controls
              whether XInclude statements in the manifest will be processed
              or not.  It defaults to False
              Note: Currently, the on-the-fly validation performed if
              validate_from_docinfo=True occurs *before* XInclude statements
              are processed and validation triggered when
              validate_from_docinfo=None or when dtd_file is specified occurs
              *after* XInclude processing.  XInclude processing may affect
              whether validation succeeds or not, so this ordering may need
              to be considered.

            Returns:
            - Nothing

            Raises:
            - ManifestError is raised if invalid values are specified
              for any paramaters or if manifest and/or dtd_file are
              specified but are not actual files
        '''

        super(ManifestParser, self).__init__(name)

        self.logger.debug("Initializing ManifestParser " \
            "(manifest=%s, validate_from_docinfo=%s, dtd_file=%s, " \
            "load_defaults=%s, call_xinclude=%s)",
            manifest, validate_from_docinfo, dtd_file,
            load_defaults, call_xinclude)

        # Check params

        # Set self._manifest from argument passed in.
        # All subsequent access to manifest will be done via  the
        # @property self.manifest.
        self._manifest = manifest

        self._validate_from_docinfo = validate_from_docinfo

        if ((dtd_file is not None) and
            (not os.path.isfile(dtd_file))):
            raise ManifestError("DTD [%s] is not a file" % dtd_file)
        self._dtd_file = dtd_file

        self._load_defaults = load_defaults

        self._call_xinclude = call_xinclude
예제 #10
0
    def parse(self, doc=None):
        '''
            This API method is not part of the AbstractCheckpoint spec.
            It can be used to access the ManifestParser functionality outside
            the InstallEngine context.

            This method is also used as a convenience function within this
            class to do most of the work of the execute() method.

            Parameters:
            - doc, a reference to the DataObjectCache in which to store
              the manifest data.  If None, the manifest data will not be
              stored anywhere, in which case this method only serves to
              confirm whether the manifest can be parsed and, optionally,
              validated.

            Returns:
            - Nothing
              On success, this method returns; on error it raises an exception.

            Raises:
            - ManifestError is raised if an error occurs in _load_manifest()
              or validate_manifest() or if
              DataObjectCache.import_from_manifest_xml() raises a ParsingError
              exception.
        '''

        self.logger.debug("ManifestParser.parse(doc=%s) called", doc)

        if self._cancel_requested.is_set():
            self.logger.debug("Cancel requested, returning.")
            return

        self.logger.debug("loading manifest (dtd_validation=%s)",
            self._validate_from_docinfo)
        tree = self._load_manifest(dtd_validation=self._validate_from_docinfo,
            attribute_defaults=self._load_defaults)

        if self._cancel_requested.is_set():
            self.logger.debug("Cancel requested, returning.")
            return

        if self._validate_from_docinfo is None:
            if ((tree.docinfo is not None) and
                (tree.docinfo.system_url is not None)):
                validate_manifest(tree, tree.docinfo.system_url, self.logger)

        if self._dtd_file is not None:
            validate_manifest(tree, self._dtd_file, self.logger)

        if self._cancel_requested.is_set():
            self.logger.debug("Cancel requested, returning.")
            return

        if doc is not None:
            # import the Manifest data into the Volatile sub-tree
            # of the DataObjectCache

            try:
                doc.import_from_manifest_xml(tree.getroot(), volatile=True)
            except ParsingError, error:
                msg = "Unable to import manifest"
                self.logger.debug(msg)
                self.logger.debug(traceback.format_exc())
                raise ManifestError(msg, orig_exception=error)