def verifyCriteriaDict(schema, criteria_dict, db, table=AIdb.MANIFESTS_TABLE):
    """
    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
                table - database table, distinguishing manifests from profiles
    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, table):
            # 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, errors = verifyXML.verifyRelaxNGManifest(schema,
                        StringIO.StringIO(lxml.etree.tostring(root)))
    if errors:
        raise ValueError(_("Error: Criteria failed validation:\n\t%s") %
                           errors.message)
    try:
        verifyXML.prepValuesAndRanges(root, db, table)
    except ValueError, err:
        raise ValueError(_("Error:\tCriteria error: %s") % err)
    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, errors = verifyXML.verifyDTDManifest(self.AI_schema,
                                                               xml_data)

            if errors:
                err = '\n'.join(errors)
                raise ValueError(_("Error: AI manifest failed validation:\n%s")
                                 % err)

            ai_instance = self.AI_root.find(".//ai_instance")

        else:
            self.AI_root, errors = verifyXML.verifyRelaxNGManifest(schema,
                                                                   xml_data)

            if errors:
                # 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),
                                  errors.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") % errors.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)

            ai_instance = self.criteria_root.find(".//ai_manifest")

        # Set/update the name inside the DOM
        ai_instance.set("name", self.manifest_name)
    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, errors = (verifyXML.verifyRelaxNGManifest(schema,
            StringIO.StringIO(lxml.etree.tostring(crit.getroot()))))
    logging.debug('criteria file passed RNG validation')

    if errors:
        raise ValueError(_("Error:\tFile %s failed validation:\n"
                           "\tline %s: %s") % (criteria_path, errors.line,
                           errors.message))
    try:
        verifyXML.prepValuesAndRanges(root, db, table)
    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)
def verifyCriteriaDict(schema, criteria_dict, db, table=AIdb.MANIFESTS_TABLE):
    """
    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
                table - database table, distinguishing manifests from profiles
    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, table):
            # 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, errors = verifyXML.verifyRelaxNGManifest(
        schema, StringIO.StringIO(lxml.etree.tostring(root)))
    if errors:
        raise ValueError(
            _("Error: Criteria failed validation:\n\t%s") % errors.message)
    try:
        verifyXML.prepValuesAndRanges(root, db, table)
    except ValueError, err:
        raise ValueError(_("Error:\tCriteria error: %s") % err)
    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, errors = verifyXML.verifyDTDManifest(
                self.AI_schema, xml_data)

            if errors:
                err = '\n'.join(errors)
                raise ValueError(
                    _("Error: AI manifest failed validation:\n%s") % err)

            ai_instance = self.AI_root.find(".//ai_instance")

        else:
            self.AI_root, errors = verifyXML.verifyRelaxNGManifest(
                schema, xml_data)

            if errors:
                # 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), errors.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") % errors.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)

            ai_instance = self.criteria_root.find(".//ai_manifest")

        # Set/update the name inside the DOM
        ai_instance.set("name", self.manifest_name)
    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, errors = (verifyXML.verifyRelaxNGManifest(
        schema, StringIO.StringIO(lxml.etree.tostring(crit.getroot()))))
    logging.debug('criteria file passed RNG validation')

    if errors:
        raise ValueError(
            _("Error:\tFile %s failed validation:\n"
              "\tline %s: %s") % (criteria_path, errors.line, errors.message))
    try:
        verifyXML.prepValuesAndRanges(root, db, table)
    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)