def send_needed_criteria(port):
    '''Replies to the old client with the needed criteria

    Args
        port - the originating port for the old client

    Returns
        None

    Raises
        None
    
    '''
    # Establish the service SQL database based upon the
    # port number for the service
    path = os.path.join(com.AI_SERVICE_DIR_PATH, str(port), 'AI.db')
    if os.path.exists(path):
        try:
            aisql = AIdb.DB(path)
            aisql.verifyDBStructure()
        except StandardError as err:
            # internal error, record the error in the server error_log
            sys.stderr.write(_('error:AI database access error\n%s\n') % err)
            # report the error to the requesting client
            print "Content-Type: text/html"    # HTML is following
            print                              # blank line, end of headers
            sys.stdout.write(_("error:AI database access error\n%s\n") % err)
            sys.exit(1)
    else:
        # not an internal error, report to the requesting client only
        print "Content-Type: text/html"    # HTML is following
        print                              # blank line, end of headers
        print _("Error:unable to determine criteria "
                "for service associated with port"), port
        return

    # build the required criteria list
    xml = lxml.etree.Element("CriteriaList")
    # old version number
    version_value = lxml.etree.Element("Version")
    version_value.attrib["Number"] = COMPATIBILITY_VERSION
    xml.append(version_value)
    # pull the required criteria from the SQL database
    for crit in AIdb.getCriteria(aisql.getQueue(), strip=True):
        tag = lxml.etree.Element("Criteria")
        tag.attrib["Name"] = crit
        xml.append(tag)
    xmlstr = lxml.etree.tostring(xml, pretty_print=True)

    # report the results
    print "Content-Length:", len(xmlstr)  # Length of XML reply
    print "Content-Type: text/xml"        # XML is following
    print                                 # blank line, end of headers
    print xmlstr
Ejemplo n.º 2
0
def send_needed_criteria(port):
    '''Replies to the old client with the needed criteria

    Args
        port - the originating port for the old client

    Returns
        None

    Raises
        None
    
    '''
    # Establish the service SQL database based upon the
    # port number for the service
    path = os.path.join(com.AI_SERVICE_DIR_PATH, str(port), 'AI.db')
    if os.path.exists(path):
        try:
            aisql = AIdb.DB(path)
            aisql.verifyDBStructure()
        except StandardError as err:
            # internal error, record the error in the server error_log
            sys.stderr.write(_('error:AI database access error\n%s\n') % err)
            # report the error to the requesting client
            print "Content-Type: text/html"  # HTML is following
            print  # blank line, end of headers
            sys.stdout.write(_("error:AI database access error\n%s\n") % err)
            sys.exit(1)
    else:
        # not an internal error, report to the requesting client only
        print "Content-Type: text/html"  # HTML is following
        print  # blank line, end of headers
        print _("Error:unable to determine criteria "
                "for service associated with port"), port
        return

    # build the required criteria list
    xml = lxml.etree.Element("CriteriaList")
    # old version number
    version_value = lxml.etree.Element("Version")
    version_value.attrib["Number"] = COMPATIBILITY_VERSION
    xml.append(version_value)
    # pull the required criteria from the SQL database
    for crit in AIdb.getCriteria(aisql.getQueue(), strip=True):
        tag = lxml.etree.Element("Criteria")
        tag.attrib["Name"] = crit
        xml.append(tag)
    xmlstr = lxml.etree.tostring(xml, pretty_print=True)

    # report the results
    print "Content-Length:", len(xmlstr)  # Length of XML reply
    print "Content-Type: text/xml"  # XML is following
    print  # blank line, end of headers
    print xmlstr
Ejemplo n.º 3
0
 def manifest_html(self):
     """
     This is manifest.html the human useable form of the manifest.xml
     special object to list needed criteria or return a manifest given a
     set of criteria
     """
     web_page = \
             E.HTML(
                    E.HEAD(
                           E.TITLE(_("%s A/I Webserver -- "
                                     "Maninfest Criteria Test") %
                                     _DISTRIBUTION)
                    ),
                    E.BODY(
                           E.H1(_("Welcome to the %s A/I "
                                  "webserver") % _DISTRIBUTION),
                           E.H2(_("Manifest criteria tester")),
                           E.P(_("To test a system's criteria, all "
                                 "criteria listed are necessary. The "
                                 "format used should be:"),
                               E.BR(),
                               E.TT("criteria1=value1;criteria2=value2"),
                               E.BR(), _("For example:"),
                               E.BR(),
                               E.TT("arch=sun4u;mac=EEE0C0FFEE00;"
                                    "ipv4=172020025012;"
                                    "manufacturer=sun microsystems")
                           ),
                           E.H1(_("Criteria:")),
                           E.P(str(list(AIdb.getCriteria(
                               self.AISQL.getQueue(), strip=True)))),
                           E.FORM(E.INPUT(type="text", name="postData"),
                                  E.INPUT(type="submit"),
                                  action="manifest.xml",
                                  method="POST"
                           )
                    )
             )
     return lxml.etree.tostring(web_page, pretty_print=True)
def prepValuesAndRanges(criteriaRoot, database, table=AIdb.MANIFESTS_TABLE):
# =============================================================================
    """
    Processes criteria manifest data already read into memory but before
    it is stored in the AI database.

    Does the following:
    - When a criterion range of one is given by a single <value>, morph
            that value so it can be stored in the database as a <range>.
    - Pad the digit strings of MAC addresses so that the six values between
            the colons are two hex digits each.
    - Pad the digit strings of IP addresses so that the four values between
            the dots are three digits each and between 0 and 255.
    - Strip off colons from MAC addresses and dots from IPv4 addresses.

    Args:
      - criteriaRoot: Tree root for criteria manifest.  This is where
            data is checked / modified.
      - database: Used to find which criteria are range criteria.

    Returns: Nothing.  However, data may be checked and modified per above.

    Raises:
    - Exception: ValueError - a range criteria provided as a list of values
                            - a non-range criteria provided as a range
    - Exceptions raised by database calls, and calls to
            - checkIPv4()
            - checkMAC()
    """
# =============================================================================

    # Find from the database which criteria are range criteria.
    # Range criteria named xxx have names bounds values MINxxx and MAXxxx.
    # Assume that MINxxx is a good enough check.
    # All criteria names in database are stored as lower case, except
    # for their "MIN" and "MAX" prefixes.
    range_crit = list()
    for crit_name in AIdb.getCriteria(database.getQueue(), table,
        onlyUsed=False, strip=False):
        if (crit_name.startswith("MIN")):
            range_crit.append(crit_name.replace("MIN", "", 1))

    # Loop through all criteria elements.
    for crit in criteriaRoot.findall('.//ai_criteria'):
        crit_name = crit.attrib['name'].lower()
        val_range = crit.getchildren()[0]

        # <range>'s here are a single element with a single
        # string containing two space-separated values for MIN and MAX
        # <value>'s here are a single element with a single
        # string containing one value or a space-separated list of values.
        value_list = val_range.text.split()
        num_values = len(value_list)

        # val_range.tag will be either 'value' or 'range'.
        # This is syntactically validated by the schema.
        if val_range.tag == "value":
            # Allow a list of values to be provided with the 'value' tag.
            # However, for criteria that can be provided as a range, we
            # currently do not support lists for them.
            if num_values > 1 and crit_name in range_crit:
                raise ValueError("Criteria '" + crit_name + "' is not "
                    "supported to be provided as a list of values")
        else:
            # For ranges, make sure it is indeed a range criteria
            if crit_name not in range_crit:
                raise ValueError("Criteria '" + crit_name + "' can not "
                    "be passed as a range pair")

        # For value criteria, there is no need to do anything to store
        # single value into val_range.text.  It is already there.
        # Just check architecture and cpu values because it is common to
        # confuse i86pc and i386 or sparc and sun4v
        if crit_name == "arch":
            for one_value in value_list:
                try:
                    checkArch(one_value)
                except ValueError as err:
                    # Just print warning
                    print >> sys.stderr, err
        elif crit_name == "cpu":
            for one_value in value_list:
                try:
                    checkCPU(one_value)
                except ValueError as err:
                    # Just print warning
                    print >> sys.stderr, err
        #
        # For some types supported by range criteria, some additional
        # format checking is needed.  Also, range criteria that are passed
        # as single values need to be split into a range where min=max.

        # Current criterion is a range criterion.
        if range_crit.count(crit_name) > 0:

            # Each value will have already been checked against the
            # schema.  IPv4 values will be 4 numbers ranging from
            # 0-255, separated by dots.  MAC values will be 6 hex
            # numbers ranging from 0-FF, separated by colons.
            # There may be one or two values.

            new_values = ""
            for one_value in value_list:

                # Space between (range) values.
                if new_values != "":
                    new_values += " "

                # Handle "unbounded" keyword; and pass lowercase
                lowered_value = one_value.lower()
                if lowered_value == "unbounded":
                    new_values += lowered_value

                # Handle IPv4 addressses.
                elif crit_name == "ipv4" or crit_name == "network":
                    new_values += checkIPv4(one_value)

                # Handle MAC addresses.
                elif crit_name == "mac":
                    new_values += checkMAC(one_value)

                # Handle everything else by passing through.
                else:
                    new_values += one_value

                # Single values which come in under a "value"
                # tag but represent a range (e.g. a single ipv4
                # value) are "converted" into the form a range
                # value pair would take (a single string
                # consisting of two items) where
                # the min value = max value.
                if val_range.tag == "value":
                    # Change to a range.
                    # Set min = max = value.
                    val_range.tag = "range"
                    val_range.text = \
                        new_values + " " + new_values
                elif val_range.tag == "range":
                    # values will be a list of 2 already.
                    val_range.text = new_values
Ejemplo n.º 5
0
def set_criteria(criteria, iname, dbn, table, 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(dbn.getQueue(),
                                 table=table,
                                 onlyUsed=False,
                                 strip=True):

        # Determine if this crit is a range criteria or not.
        is_range_crit = AIdb.isRangeCriteria(dbn.getQueue(), crit)

        # Get the value from the manifest
        values = criteria[crit]

        # the criteria 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 is_range_crit:
                    nvpairs.append("MIN" + crit + "=NULL")
                    nvpairs.append("MAX" + crit + "=NULL")
                # this is a single value
                else:
                    nvpairs.append(crit + "=NULL")

        # Else if this is a value criteria (not a range), insert the
        # value as a space-separated list of values in case a list of
        # values have been given.
        elif not is_range_crit:
            nvstr = crit + "='" + AIdb.sanitizeSQL(" ".join(values)) + "'"
            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 + "=" +
                           AIdb.format_value(crit, values[0]))

            # Set the MAX column for this range criteria
            nvpairs.append("MAX" + crit + "=" +
                           AIdb.format_value(crit, values[1]))

    query = "UPDATE " + table + " SET " + ",".join(nvpairs) + \
            " WHERE name='" + iname + "'"

    # update the DB
    query = AIdb.DBrequest(query, commit=True)
    dbn.getQueue().put(query)
    query.waitAns()
    # in case there's an error call the response function (which
    # will print the error)
    query.getResponse()
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 already 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,"

        # Else if this is a value criteria (not a range), insert the value
        # as a space-separated list of values which will account for the case
        # where a list of values have been given.
        elif not crit.startswith('MAX'):
            # Join the values of the list with a space separator.
            query += "'" + AIdb.sanitizeSQL(" ".join(values)) + "',"
        # 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()
Ejemplo n.º 7
0
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]

        # Determine if this crit is a range criteria or not.
        is_range_crit = AIdb.isRangeCriteria(db.getQueue(), crit,
                                             AIdb.MANIFESTS_TABLE)

        # Process "value" criteria here (check if the criteria exists in
        # DB, and then find collisions)
        if not is_range_crit:
            # 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 a value in the list of values to be added is equal
                # to a value in the list of values for this criteria for this
                # row
                for value in man_criterion:
                    if AIdb.is_in_list(crit, value, str(row[Fields.CRIT]),
                                       None):
                        # 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:
            # Clean-up NULL's and change "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 for a properly ordered range (with unbounded being 0 or
            # Inf.) but ensure both are not unbounded.
            # Check for:
            #       a range of zero to inf -- not a valid range
            #  and
            #       min > max -- range order reversed
            #
            if (man_criterion[0] == 0 and man_criterion[1] == long(INFINITY)):
                raise SystemExit(
                    _("Error:\tCriteria %s is not a valid range, "
                      "MIN and MAX unbounded.") % crit)

            if ((man_criterion[0] != 0 and man_criterion[1] != long(INFINITY))
                    and (long(man_criterion[0]) > long(man_criterion[1]))):
                raise SystemExit(
                    _("Error:\tCriteria %s is not a valid range, "
                      "MIN > MAX.") % crit)
            # check to see that this criteria exists in the database columns
            man_crit = AIdb.getCriteria(db.getQueue(),
                                        onlyUsed=False,
                                        strip=False)
            if 'MIN' + crit not in man_crit and 'MAX' + crit not in man_crit:
                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
Ejemplo n.º 8
0
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)))

            # Either the range did not collide or this is not a range
            # criteria.  (If the value of this criteria in the db does
            # not equal the value of this criteria for the set of criteria
            # to check, we can break out knowing we diverge for this
            # manifest/instance)
            elif not db_criterion and not man_criterion:
                # Neither the value for this criteria in the db nor
                # the value for for this criteria in the given set of
                # criteria to check are populated.  Loop around to
                # check the next criteria.
                continue
            elif not db_criterion or not man_criterion:
                # One of the two are not populated, we can break knowing
                # they're different.
                break
            else:
                # Both are populated.  If none of values in the list for
                # this criteria to be added are equal to any of the values
                # in the list for this criteria from the db, there will be
                # no collision.  We can break out.
                if not [value for value in man_criterion if \
                    AIdb.is_in_list(crit, value, str(db_criterion), None)]:
                    break

        # end of for loop and we never broke out (collision)
        else:
            raise SystemExit(
                _("Error:\tManifest has same criteria as " +
                  "manifest: %s/%i!") % (man_inst[0], man_inst[1]))
def sql_values_from_criteria(criteria, queue, table, gbl=False):
    ''' Given a criteria dictionary, for the indicated DB table
    and queue, return a tuple composed of lists whose elements can be used
    to construct SQLite clauses.  If gbl is true, build a clause that
    will affect all database records if criteria is missing - a global effect.
    Args:
        criteria - criteria dictionary
        queue - database queue
        table - database table
        gbl - if True, global
    Returns: a tuple for SQLite clauses respectively: WHERE, INTO, VALUES
    '''
    where = list()  # for WHERE clause
    intol = list()  # for INTO clause
    vals = list()  # for VALUES clause
    for crit in AIdb.getCriteria(queue, table, onlyUsed=False, strip=True):

        # Determine if this crit is a range criteria or not.
        is_range_crit = AIdb.isRangeCriteria(queue, crit, table)

        # Get the value from the manifest
        values = criteria[crit]
        # the critera manifest didn't specify this criteria
        if values is None:
            # if the criteria we're processing is a range criteria, fill in
            # NULL for two columns, MINcrit and MAXcrit
            vals += ["NULL"]
            if is_range_crit:
                where += ["MIN" + crit + " IS NULL"]
                where += ["MAX" + crit + " IS NULL"]
                intol += ["MIN" + crit]
                intol += ["MAX" + crit]
                vals += ["NULL"]
            # this is a single value
            else:
                where += [crit + " IS NULL"]
                intol += [crit]
        # This is a value criteria (not a range).  'values' is a list
        # with one or more items.
        elif not is_range_crit:
            intol += [crit]
            val = AIdb.format_value(crit, " ".join(values))
            where += [crit + "=" + val]
            vals += [val]
        # Else this is a range criteria.  'values' is a two-item list
        else:
            # Set the MIN column for this range criteria
            if values[0] == 'unbounded':
                if not gbl:
                    where += ["MIN" + crit + " IS NULL"]
                    intol += ["MIN" + crit]
                    vals += ['NULL']
            else:
                intol += ["MIN" + crit]
                if crit == 'mac':
                    val = AIdb.format_value(crit,
                            verifyXML.checkMAC(values[0])).upper()
                    where += ["HEX(MIN" + crit + ")<=HEX(" + val + ")"]
                else:
                    val = AIdb.format_value(crit, values[0]).lower()
                    where += ["MIN" + crit + "<=" + val]
                vals += [val]
            # Set the MAX column for this range criteria
            if values[1] == 'unbounded':
                if not gbl:
                    where += ["MAX" + crit + " IS NULL"]
                    intol += ["MAX" + crit]
                    vals += ['NULL']
            else:
                intol += ["MAX" + crit]
                if crit == 'mac':
                    val = AIdb.format_value(crit,
                            verifyXML.checkMAC(values[1])).upper()
                    where += ["HEX(MAX" + crit + ")>=HEX(" + val + ")"]
                else:
                    val = AIdb.format_value(crit, values[1]).lower()
                    where += ["MAX" + crit + ">=" + val]
                vals += [val]
    return where, intol, vals
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)))

            # Either the range did not collide or this is not a range
            # criteria.  (If the value of this criteria in the db does
            # not equal the value of this criteria for the set of criteria
            # to check, we can break out knowing we diverge for this
            # manifest/instance)
            elif not db_criterion and not man_criterion:
                # Neither the value for this criteria in the db nor
                # the value for for this criteria in the given set of
                # criteria to check are populated.  Loop around to
                # check the next criteria.
                continue
            elif not db_criterion or not man_criterion:
                # One of the two are not populated, we can break knowing
                # they're different.
                break
            else:
                # Both are populated.  If none of values in the list for
                # this criteria to be added are equal to any of the values
                # in the list for this criteria from the db, there will be
                # no collision.  We can break out.
                if not [value for value in man_criterion if \
                    AIdb.is_in_list(crit, value, str(db_criterion), None)]:
                    break

        # end of for loop and we never broke out (collision)
        else:
            raise SystemExit(_("Error:\tManifest has same criteria as " +
                               "manifest: %s/%i!") %
                             (man_inst[0], man_inst[1]))
Ejemplo n.º 11
0
def prepValuesAndRanges(criteriaRoot, database):
    # ==============================================================================
    """
    Processes criteria manifest data already read into memory but before
    it is stored in the AI database.

    Does the following:
    - When a criterion range of one is given by a single <value>, morph
            that value so it can be stored in the database as a <range>.
    - Pad the digit strings of MAC addresses so that the six values between
            the colons are two hex digits each.
    - Pad the digit strings of IP addresses so that the four values between
            the dots are three digits each and between 0 and 255.
    - Strip off colons from MAC addresses and dots from IPv4 addresses.

    Args:
      - criteriaRoot: Tree root for criteria manifest.  This is where
            data is checked / modified.
      - database: Used to find which criteria are range criteria.

    Returns: Nothing.  However, data may be checked and modified per above.

    Raises:
    - Exception: Exactly 1 value (no spaces) expected for cpu criteria tag
    - Exceptions raised by database calls, and calls to
            - __checkIPv4()
            - __checkMAC()
    """
    # ==============================================================================

    # Find from the database which criteria are range criteria.
    # Range criteria named xxx have names bounds values MINxxx and MAXxxx.
    # Assume that MINxxx is a good enough check.
    # All criteria names in database are stored as lower case, except
    # for their "MIN" and "MAX" prefixes.
    range_crit = []
    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))

    # Loop through all criteria elements.
    for crit in criteriaRoot.findall('.//ai_criteria'):
        crit_name = crit.attrib['name'].lower()
        val_range = crit.getchildren()[0]

        # <range>'s here are a single element with a single
        # string containing two space-separated values for MIN and MAX
        # <value>'s here are a single element with a single
        # string containing one value.
        value_list = val_range.text.split()
        num_values = len(value_list)

        # Val_range.tag will be either value or range.
        # This is checked by the schema.
        if (val_range.tag == "value"):

            # Allow values with spaces (which here look like
            # multiple values), except for CPU items.  Non-CPU
            # items are "arch" and "platform".
            if ((num_values != 1) and (crit_name == "cpu")):
                raise Exception, ("Exactly 1 value " +
                                  "(no spaces) expected for cpu criteria tag")
        else:
            if (range_crit.count(crit_name) == 0):
                raise Exception, ("Range pair passed to " +
                                  "non-range criterion \"" + crit_name + "\"")

        # For value criteria, there is no need to do anything to store
        # single value into val_range.text.  It is already there.
        #
        # For some types supported by range criteria, some additional
        # format checking is needed.  Also, single values passed as
        # range criteria need to be split into a range where min=max.

        # Current criterion is a range criterion.
        if (range_crit.count(crit_name) > 0):

            # Each value will have already been checked against the
            # schema.  IPv4 values will be 4 numbers ranging from
            # 0-255, separated by dots.  MAC values will be 6 hex
            # numbers ranging from 0-FF, separated by colons.
            # There may be one or two values.

            new_values = ""
            for one_value in value_list:

                # Space between (range) values.
                if (new_values != ""):
                    new_values += " "

                # Handle "unbounded" keyword; and pass lowercase
                lowered_value = one_value.lower()
                if (lowered_value == "unbounded"):
                    new_values += lowered_value

                # Handle IPv4 addressses.
                elif (crit_name == "ipv4"):
                    new_values += __checkIPv4(one_value)

                # Handle MAC addresses.
                elif (crit_name == "mac"):
                    new_values += __checkMAC(one_value)

                # Handle everything else by passing through.
                else:
                    new_values += one_value

                # Single values which come in under a "value"
                # tag but represent a range (e.g. a single ipv4
                # value) are "converted" into the form a range
                # value pair would take (a single string
                # consisting of two items) where
                # the min value = max value.
                if (val_range.tag == "value"):
                    # Change to a range.
                    # Set min = max = value.
                    val_range.tag = "range"
                    val_range.text = \
                        new_values + " " + new_values
                elif (val_range.tag == "range"):
                    # values will be a list of 2 already.
                    val_range.text = new_values
Ejemplo n.º 12
0
def send_manifest(form_data,
                  port=0,
                  servicename=None,
                  protocolversion=COMPATIBILITY_VERSION,
                  no_default=False):
    '''Replies to the client with matching service for a service.
    
    Args
        form_data   - the postData passed in from the client request
        port        - the port of the old client
        servicename - the name of the service being used
        protocolversion - the version of the AI service RE: handshake
        no_default  - boolean flag to signify whether or not we should hand
                      back the default manifest and profiles if one cannot
                      be matched based on the client criteria.

    Returns
        None
    
    Raises
        None
    
    '''
    # figure out the appropriate path for the AI database,
    # and get service name if necessary.
    # currently service information is stored in a port directory.
    # When the cherrypy webserver new service directories should be
    # separated via service-name only.  Old services will still use
    # port numbers as the separation mechanism.
    path = None
    found_servicename = None
    service = None
    port = str(port)

    if servicename:
        service = AIService(servicename)
        path = service.database_path
    else:
        for name in config.get_all_service_names():
            if config.get_service_port(name) == port:
                found_servicename = name
                service = AIService(name)
                path = service.database_path
                break

    # Check to insure that a valid path was found
    if not path or not os.path.exists(path):
        print 'Content-Type: text/html'  # HTML is following
        print  # blank line, end of headers
        if servicename:
            print '<pre><b>Error</b>:unable to find<i>', servicename + '</i>.'
        else:
            print '<pre><b>Error</b>:unable to find<i>', port + '</i>.'
        print 'Available services are:<p><ol><i>'
        hostname = socket.gethostname()
        for name in config.get_all_service_names():
            port = config.get_service_port(name)
            sys.stdout.write(
                '<a href="http://%s:%d/cgi-bin/'
                'cgi_get_manifest.py?version=%s&service=%s">%s</a><br>\n' %
                (hostname, port, VERSION, name, name))
        print '</i></ol>Please select a service from the above list.'
        return

    if found_servicename:
        servicename = found_servicename

    # load to the AI database
    aisql = AIdb.DB(path)
    aisql.verifyDBStructure()

    # convert the form data into a criteria dictionary
    criteria = dict()
    orig_data = form_data
    while form_data:
        try:
            [key_value, form_data] = form_data.split(';', 1)
        except (ValueError, NameError, TypeError, KeyError):
            key_value = form_data
            form_data = ''
        try:
            [key, value] = key_value.split('=')
            criteria[key] = value
        except (ValueError, NameError, TypeError, KeyError):
            criteria = dict()

    # Generate templating dictionary from criteria
    template_dict = dict()
    for crit in criteria:
        template_dict["AI_" + crit.upper()] = \
                AIdb.formatValue(crit, criteria[crit], units=False)

    # find the appropriate manifest
    try:
        manifest = AIdb.findManifest(criteria, aisql)
    except StandardError as err:
        print 'Content-Type: text/html'  # HTML is following
        print  # blank line, end of headers
        print '<pre><b>Error</b>:findManifest criteria<br>'
        print err, '<br>'
        print '<ol>servicename =', servicename
        print 'port        =', port
        print 'path        =', path
        print 'form_data   =', orig_data
        print 'criteria    =', criteria
        print 'servicename found by port =', found_servicename, '</ol>'
        print '</pre>'
        return

    # check if findManifest() returned a number equal to 0
    # (means we got no manifests back -- thus we serve the default if desired)
    if manifest is None and not no_default:
        manifest = service.get_default_manifest()

    # if we have a manifest to return, prepare its return
    if manifest is not None:
        try:
            # construct the fully qualified filename
            filename = os.path.abspath(
                os.path.join(service.manifest_dir, manifest))
            # open and read the manifest
            with open(filename, 'rb') as mfp:
                manifest_str = mfp.read()
            # maintain compability with older AI client
            if servicename is None or \
                    float(protocolversion) < float(PROFILES_VERSION):
                content_type = mimetypes.types_map.get('.xml', 'text/plain')
                print 'Content-Length:', len(
                    manifest_str)  # Length of the file
                print 'Content-Type:', content_type  # XML is following
                print  # blank line, end of headers
                print manifest_str
                logging.info('Manifest sent from %s.' % filename)
                return

        except OSError as err:
            print 'Content-Type: text/html'  # HTML is following
            print  # blank line, end of headers
            print '<pre>'
            # report the internal error to error_log and requesting client
            sys.stderr.write(_('error:manifest (%s) %s\n') % \
                            (str(manifest), err))
            sys.stdout.write(_('error:manifest (%s) %s\n') % \
                            (str(manifest), err))
            print '</pre>'
            return

    # get AI service image path
    service = AIService(servicename)
    image_dir = service.image.path
    # construct object to contain MIME multipart message
    outermime = MIMEMultipart()
    client_msg = list()  # accumulate message output for AI client

    # If we have a manifest, attach it to the return message
    if manifest is not None:
        # add manifest as attachment
        msg = MIMEText(manifest_str, 'xml')
        # indicate manifest using special name
        msg.add_header('Content-Disposition',
                       'attachment',
                       filename=sc.AI_MANIFEST_ATTACHMENT_NAME)
        outermime.attach(msg)  # add manifest as an attachment

    # search for any profiles matching client criteria
    # formulate database query to profiles table
    q_str = "SELECT DISTINCT name, file FROM " + \
        AIdb.PROFILES_TABLE + " WHERE "
    nvpairs = list()  # accumulate criteria values from post-data
    # for all AI client criteria
    for crit in AIdb.getCriteria(aisql.getQueue(),
                                 table=AIdb.PROFILES_TABLE,
                                 onlyUsed=False):
        if crit not in criteria:
            msgtxt = _("Warning: client criteria \"%s\" not provided in "
                       "request.  Setting value to NULL for profile lookup.") \
                       % crit
            client_msg += [msgtxt]
            logging.warn(msgtxt)
            # fetch only global profiles destined for all clients
            if AIdb.isRangeCriteria(aisql.getQueue(), crit,
                                    AIdb.PROFILES_TABLE):
                nvpairs += ["MIN" + crit + " IS NULL"]
                nvpairs += ["MAX" + crit + " IS NULL"]
            else:
                nvpairs += [crit + " IS NULL"]
            continue

        # prepare criteria value to add to query
        envval = AIdb.sanitizeSQL(criteria[crit])
        if AIdb.isRangeCriteria(aisql.getQueue(), crit, AIdb.PROFILES_TABLE):
            # If no default profiles are requested, then we mustn't allow
            # this criteria to be NULL.  It must match the client's given
            # value for this criteria.
            if no_default:
                if crit == "mac":
                    nvpairs += ["(HEX(MIN" + crit + ")<=HEX(X'" + envval + \
                        "'))"]

                    nvpairs += ["(HEX(MAX" + crit + ")>=HEX(X'" + envval + \
                        "'))"]
                else:
                    nvpairs += ["(MIN" + crit + "<='" + envval + "')"]
                    nvpairs += ["(MAX" + crit + ">='" + envval + "')"]
            else:
                if crit == "mac":
                    nvpairs += [
                        "(MIN" + crit + " IS NULL OR "
                        "HEX(MIN" + crit + ")<=HEX(X'" + envval + "'))"
                    ]
                    nvpairs += [
                        "(MAX" + crit + " IS NULL OR HEX(MAX" + crit +
                        ")>=HEX(X'" + envval + "'))"
                    ]
                else:
                    nvpairs += [
                        "(MIN" + crit + " IS NULL OR MIN" + crit + "<='" +
                        envval + "')"
                    ]
                    nvpairs += [
                        "(MAX" + crit + " IS NULL OR MAX" + crit + ">='" +
                        envval + "')"
                    ]
        else:
            # If no default profiles are requested, then we mustn't allow
            # this criteria to be NULL.  It must match the client's given
            # value for this criteria.
            #
            # Also, since this is a non-range criteria, the value stored
            # in the DB may be a whitespace separated list of single
            # values.  We use a special user-defined function in the
            # determine if the given criteria is in that textual list.
            if no_default:
                nvpairs += ["(is_in_list('" + crit + "', '" + envval + \
                    "', " + crit + ", 'None') == 1)"]
            else:
                nvpairs += ["(" + crit + " IS NULL OR is_in_list('" + crit + \
                    "', '" + envval + "', " + crit + ", 'None') == 1)"]

    if len(nvpairs) > 0:
        q_str += " AND ".join(nvpairs)

        # issue database query
        logging.info("Profile query: " + q_str)
        query = AIdb.DBrequest(q_str)
        aisql.getQueue().put(query)
        query.waitAns()
        if query.getResponse() is None or len(query.getResponse()) == 0:
            msgtxt = _("No profiles found.")
            client_msg += [msgtxt]
            logging.info(msgtxt)
        else:
            for row in query.getResponse():
                profpath = row['file']
                profname = row['name']
                if profname is None:  # should not happen
                    profname = 'unnamed'
                try:
                    if profpath is None:
                        msgtxt = "Database record error - profile path is " \
                            "empty."
                        client_msg += [msgtxt]
                        logging.error(msgtxt)
                        continue
                    msgtxt = _('Processing profile %s') % profname
                    client_msg += [msgtxt]
                    logging.info(msgtxt)
                    with open(profpath, 'r') as pfp:
                        raw_profile = pfp.read()
                    # do any template variable replacement {{AI_xxx}}
                    tmpl_profile = sc.perform_templating(
                        raw_profile, template_dict)
                    # precautionary validation of profile, logging only
                    sc.validate_profile_string(tmpl_profile,
                                               image_dir,
                                               dtd_validation=True,
                                               warn_if_dtd_missing=True)
                except IOError as err:
                    msgtxt = _("Error:  I/O error: ") + str(err)
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    continue
                except OSError:
                    msgtxt = _("Error:  OS error on profile ") + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    continue
                except KeyError:
                    msgtxt = _('Error:  could not find criteria to substitute '
                               'in template: ') + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    logging.error('Profile with template substitution error:' +
                                  raw_profile)
                    continue
                except lxml.etree.XMLSyntaxError as err:
                    # log validation error and proceed
                    msgtxt = _(
                            'Warning:  syntax error found in profile: ') \
                            + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    for error in err.error_log:
                        msgtxt = _('Error:  ') + error.message
                        client_msg += [msgtxt]
                        logging.error(msgtxt)
                    logging.info([
                        _('Profile failing validation:  ') +
                        lxml.etree.tostring(root)
                    ])
                # build MIME message and attach to outer MIME message
                msg = MIMEText(tmpl_profile, 'xml')
                # indicate in header that this is an attachment
                msg.add_header('Content-Disposition',
                               'attachment',
                               filename=profname)
                # attach this profile to the manifest and any other profiles
                outermime.attach(msg)
                msgtxt = _('Parsed and loaded profile: ') + profname
                client_msg += [msgtxt]
                logging.info(msgtxt)

    # any profiles and AI manifest have been attached to MIME message
    # specially format list of messages for display on AI client console
    if client_msg:
        outtxt = ''
        for msgtxt in client_msg:
            msgtxt = _('SC profile locator:') + msgtxt
            outtxt += str(msgtxt) + '\n'
        # add AI client console messages as single plain text attachment
        msg = MIMEText(outtxt, 'plain')  # create MIME message
        outermime.attach(msg)  # attach MIME message to response

    print outermime.as_string()  # send MIME-formatted message
Ejemplo n.º 13
0
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 already 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,"

        # Else if this is a value criteria (not a range), insert the value
        # as a space-separated list of values which will account for the case
        # where a list of values have been given.
        elif not crit.startswith('MAX'):
            # Join the values of the list with a space separator.
            query += "'" + AIdb.sanitizeSQL(" ".join(values)) + "',"
        # 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()
Ejemplo n.º 14
0
def validate_criteria_from_user(criteria, dbo, table):
    ''' Validate profile criteria from dictionary containing command line input
    Args:    criteria - Criteria object holding the criteria that is to be
                        added/set for a manifest.
             dbo - AI_database object for the install service.
             table - name of database table
    Raises:  SystemExit if:
        - criteria is not found in database
        - value is not valid for type (integer and hexadecimal checks)
        - range is improper
    Returns: nothing
    '''
    # find all possible profile criteria expressed as DB table columns
    critlist = []
    # take criteria from generator
    for crit in AIdb.getCriteria(dbo.getQueue(),
                                 table,
                                 onlyUsed=False,
                                 strip=False):
        critlist.append(crit)
    # verify each range criteria is well formed
    for crit in criteria:
        # gather this criteria's values
        man_criterion = criteria[crit]
        # check "value" criteria here (check the criteria exists in DB
        if not AIdb.isRangeCriteria(dbo.getQueue(), crit, table):
            # only check criteria in use in the DB
            if crit not in critlist:
                raise SystemExit(
                    _("Error:\tCriteria %s is not a valid criteria!") % 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
        else:
            # check for a properly ordered range (with unbounded being 0 or
            # Inf.)
            if man_criterion[0] != "unbounded" and \
                man_criterion[1] != "unbounded" and \
                man_criterion[0] > man_criterion[1]:  # Check min > max
                raise SystemExit(
                    _("Error:\tCriteria %s is not a valid range (MIN > MAX) ")
                    % crit)
            # Clean-up NULL's and changed "unbounded"s to 0 and
            # the maximum integer value
            # Note "unbounded"s are already converted to lower case during
            # input processing
            if man_criterion[0] == "unbounded":
                man_criterion[0] = "0"
            if man_criterion[1] == "unbounded":
                man_criterion[1] = str(sys.maxint)
            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 critlist and 'MAX' + crit not in critlist:
                raise SystemExit(
                    _("Error:\tCriteria %s is not a valid criteria!") % crit)
def set_criteria(criteria, iname, dbn, table, 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(dbn.getQueue(), table=table, onlyUsed=False,
                                 strip=True):

        # Determine if this crit is a range criteria or not.
        is_range_crit = AIdb.isRangeCriteria(dbn.getQueue(), crit)

        # Get the value from the manifest
        values = criteria[crit]

        # the criteria 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 is_range_crit:
                    nvpairs.append("MIN" + crit + "=NULL")
                    nvpairs.append("MAX" + crit + "=NULL")
                # this is a single value
                else:
                    nvpairs.append(crit + "=NULL")

        # Else if this is a value criteria (not a range), insert the
        # value as a space-separated list of values in case a list of
        # values have been given. 
        elif not is_range_crit:
            nvstr = crit + "='" + AIdb.sanitizeSQL(" ".join(values)) + "'"
            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 + "=" +
                           AIdb.format_value(crit, values[0]))

            # Set the MAX column for this range criteria
            nvpairs.append("MAX" + crit + "=" +
                           AIdb.format_value(crit, values[1]))

    query = "UPDATE " + table + " SET " + ",".join(nvpairs) + \
            " WHERE name='" + iname + "'"

    # update the DB
    query = AIdb.DBrequest(query, commit=True)
    dbn.getQueue().put(query)
    query.waitAns()
    # in case there's an error call the response function (which
    # will print the error)
    query.getResponse()
def list_manifests(service):
    '''Replies to the client with criteria list for a service.
       The output should be similar to installadm list.

    Args
        service - the name of the service being listed

    Returns
        None

    Raises
        None
    '''
    print 'Content-Type: text/html'     # HTML is following
    print                               # blank line, end of headers
    print '<html>'
    print '<head>'
    sys.stdout.write('<title>%s %s</title>' %
                     (_('Manifest list for'), service))
    print '</head><body>'

    port = 0
    try:
        smf.AISCF(FMRI="system/install/server")
    except KeyError:
        # report the internal error to error_log and requesting client
        sys.stderr.write(_("error:The system does not have the "
                           "system/install/server SMF service."))
        sys.stdout.write(_("error:The system does not have the "
                           "system/install/server SMF service."))
        return
    services = config.get_all_service_names()
    if not services:
        # report the error to the requesting client only
        sys.stdout.write(_('error:no services on this server.\n'))
        return

    found = False
    if config.is_service(service):
        service_ctrl = AIService(service)
        found = True

        # assume new service setup
        path = service_ctrl.database_path
        if os.path.exists(path):
            try:
                aisql = AIdb.DB(path)
                aisql.verifyDBStructure()
            except StandardError as err:
                # report the internal error to error_log and
                # requesting client
                sys.stderr.write(_('error:AI database access '
                                   'error\n%s\n') % err)
                sys.stdout.write(_('error:AI database access '
                                   'error\n%s\n') % err)
                return

            # generate the list of criteria for the criteria table header
            criteria_header = E.TR()
            for crit in AIdb.getCriteria(aisql.getQueue(), strip=False):
                criteria_header.append(E.TH(crit))

            # generate the manifest rows for the criteria table body
            names = AIdb.getManNames(aisql.getQueue())
            table_body = E.TR()
            allcrit = AIdb.getCriteria(aisql.getQueue(), strip=False)
            colspan = str(max(len(list(allcrit)), 1))
            for manifest in names:

                # iterate through each manifest (and instance)
                for instance in range(0,
                        AIdb.numInstances(manifest, aisql.getQueue())):

                    table_body.append(E.TR())
                    # print the manifest name only once (from instance 0)
                    if instance == 0:
                        href = '../' + service + '/' + manifest
                        row = str(AIdb.numInstances(manifest,
                                                    aisql.getQueue()))
                        table_body.append(E.TD(
                                   E.A(manifest, href=href, rowspan=row)))
                    else:
                        table_body.append(E.TD())

                    crit_pairs = AIdb.getManifestCriteria(manifest,
                                                          instance,
                                                          aisql.getQueue(),
                                                          onlyUsed=True,
                                                          humanOutput=True)

                    # crit_pairs is an SQLite3 row object which doesn't
                    # support iteritems(), etc.
                    for crit in crit_pairs.keys():
                        formatted_val = AIdb.formatValue(crit,
                                                         crit_pairs[crit])
                        # if we do not get back a valid value ensure a
                        # hyphen is printed (prevents "" from printing)
                        if formatted_val and crit_pairs[crit]:
                            table_body.append(E.TD(formatted_val,
                                                   align="center"))
                        else:
                            table_body.append(E.TD(lxml.etree.Entity("nbsp"),
                                                   align="center"))

            # print the default manifest at the end of the table,
            # which has the same colspan as the Criteria List label
            else:
                href = '../' + service + '/default.xml'
                table_body.append(E.TR(E.TD(E.A("Default", href=href)),
                                       E.TD(lxml.etree.Entity("nbsp"),
                                            colspan=colspan,
                                            align="center")))
            web_page = E.HTML(
                         E.HEAD(
                            E.TITLE(_("OmniOS Automated "
                                      "Installation Webserver"))
                            ),
                         E.BODY(
                            E.H1(_("Welcome to the OmniOS "
                                   "Automated Installation webserver!")),
                            E.P(_("Service '%s' has the following "
                                "manifests available, served to clients "
                                "matching required criteria.") % service),
                            E.TABLE(
                               E.TR(
                                    E.TH(_("Manifest"), rowspan="2"),
                                    E.TH(_("Criteria List"),
                                        colspan=colspan)),
                                    criteria_header,
                                    table_body,
                                    border="1", align="center"),
                          )
                     )
            print lxml.etree.tostring(web_page, pretty_print=True)

    # service is not found, provide available services on host
    if not found:
        sys.stdout.write(_('Service <i>%s</i> not found.  ') % service)
        sys.stdout.write(_('Available services are:<p><ol><i>'))
        host = socket.gethostname()
        for service_name in config.get_all_service_names():
            # assume new service setup
            port = config.get_service_port(service_name)
            sys.stdout.write('<a href="http://%s:%d/cgi-bin/'
                   'cgi_get_manifest.py?version=%s&service=%s">%s</a><br>\n' %
                   (host, port, VERSION, service_name, service_name))
        sys.stdout.write('</i></ol>%s' % _('Please select a service '
                   'from the above list.'))

    print '</body></html>'
def send_manifest(form_data, port=0, servicename=None,
        protocolversion=COMPATIBILITY_VERSION, no_default=False):
    '''Replies to the client with matching service for a service.
    
    Args
        form_data   - the postData passed in from the client request
        port        - the port of the old client
        servicename - the name of the service being used
        protocolversion - the version of the AI service RE: handshake
        no_default  - boolean flag to signify whether or not we should hand
                      back the default manifest and profiles if one cannot
                      be matched based on the client criteria.

    Returns
        None
    
    Raises
        None
    
    '''
    # figure out the appropriate path for the AI database,
    # and get service name if necessary.
    # currently service information is stored in a port directory.
    # When the cherrypy webserver new service directories should be
    # separated via service-name only.  Old services will still use
    # port numbers as the separation mechanism.
    path = None
    found_servicename = None
    service = None
    port = str(port)
    
    if servicename:
        service = AIService(servicename)
        path = service.database_path
    else:
        for name in config.get_all_service_names():
            if config.get_service_port(name) == port:
                found_servicename = name
                service = AIService(name)
                path = service.database_path
                break
    
    # Check to insure that a valid path was found
    if not path or not os.path.exists(path):
        print 'Content-Type: text/html'     # HTML is following
        print                               # blank line, end of headers
        if servicename:
            print '<pre><b>Error</b>:unable to find<i>', servicename + '</i>.'
        else:
            print '<pre><b>Error</b>:unable to find<i>', port + '</i>.'
        print 'Available services are:<p><ol><i>'
        hostname = socket.gethostname()
        for name in config.get_all_service_names():
            port = config.get_service_port(name)
            sys.stdout.write('<a href="http://%s:%d/cgi-bin/'
                   'cgi_get_manifest.py?version=%s&service=%s">%s</a><br>\n' %
                   (hostname, port, VERSION, name, name))
        print '</i></ol>Please select a service from the above list.'
        return

    if found_servicename:
        servicename = found_servicename

    # load to the AI database
    aisql = AIdb.DB(path)
    aisql.verifyDBStructure()

    # convert the form data into a criteria dictionary
    criteria = dict()
    orig_data = form_data
    while form_data:
        try:
            [key_value, form_data] = form_data.split(';', 1)
        except (ValueError, NameError, TypeError, KeyError):
            key_value = form_data
            form_data = ''
        try:
            [key, value] = key_value.split('=')
            criteria[key] = value
        except (ValueError, NameError, TypeError, KeyError):
            criteria = dict()

    # Generate templating dictionary from criteria
    template_dict = dict()
    for crit in criteria:
        template_dict["AI_" + crit.upper()] = \
                AIdb.formatValue(crit, criteria[crit], units=False)
            
    # find the appropriate manifest
    try:
        manifest = AIdb.findManifest(criteria, aisql)
    except StandardError as err:
        print 'Content-Type: text/html'     # HTML is following
        print                               # blank line, end of headers
        print '<pre><b>Error</b>:findManifest criteria<br>'
        print err, '<br>'
        print '<ol>servicename =', servicename
        print 'port        =', port
        print 'path        =', path
        print 'form_data   =', orig_data
        print 'criteria    =', criteria
        print 'servicename found by port =', found_servicename, '</ol>'
        print '</pre>'
        return

    # check if findManifest() returned a number equal to 0
    # (means we got no manifests back -- thus we serve the default if desired)
    if manifest is None and not no_default:
        manifest = service.get_default_manifest()

    # if we have a manifest to return, prepare its return
    if manifest is not None:
        try:
            # construct the fully qualified filename
            filename = os.path.abspath(os.path.join(service.manifest_dir,
                                                    manifest))
            # open and read the manifest
            with open(filename, 'rb') as mfp:
                manifest_str = mfp.read()
            # maintain compability with older AI client
            if servicename is None or \
                    float(protocolversion) < float(PROFILES_VERSION):
                content_type = mimetypes.types_map.get('.xml', 'text/plain')
                print 'Content-Length:', len(manifest_str) # Length of the file
                print 'Content-Type:', content_type        # XML is following
                print                              # blank line, end of headers
                print manifest_str
                logging.info('Manifest sent from %s.' % filename)
                return

        except OSError as err:
            print 'Content-Type: text/html'     # HTML is following
            print                               # blank line, end of headers
            print '<pre>'
            # report the internal error to error_log and requesting client
            sys.stderr.write(_('error:manifest (%s) %s\n') % \
                            (str(manifest), err))
            sys.stdout.write(_('error:manifest (%s) %s\n') % \
                            (str(manifest), err))
            print '</pre>'
            return

    # get AI service image path
    service = AIService(servicename)
    image_dir = service.image.path
    # construct object to contain MIME multipart message
    outermime = MIMEMultipart()
    client_msg = list()  # accumulate message output for AI client

    # If we have a manifest, attach it to the return message
    if manifest is not None:
        # add manifest as attachment
        msg = MIMEText(manifest_str, 'xml')
        # indicate manifest using special name
        msg.add_header('Content-Disposition', 'attachment',
                      filename=sc.AI_MANIFEST_ATTACHMENT_NAME)
        outermime.attach(msg)  # add manifest as an attachment

    # search for any profiles matching client criteria
    # formulate database query to profiles table
    q_str = "SELECT DISTINCT name, file FROM " + \
        AIdb.PROFILES_TABLE + " WHERE "
    nvpairs = list()  # accumulate criteria values from post-data
    # for all AI client criteria
    for crit in AIdb.getCriteria(aisql.getQueue(), table=AIdb.PROFILES_TABLE,
                                 onlyUsed=False):
        if crit not in criteria:
            msgtxt = _("Warning: client criteria \"%s\" not provided in "
                       "request.  Setting value to NULL for profile lookup.") \
                       % crit
            client_msg += [msgtxt]
            logging.warn(msgtxt)
            # fetch only global profiles destined for all clients
            if AIdb.isRangeCriteria(aisql.getQueue(), crit,
                                    AIdb.PROFILES_TABLE):
                nvpairs += ["MIN" + crit + " IS NULL"]
                nvpairs += ["MAX" + crit + " IS NULL"]
            else:
                nvpairs += [crit + " IS NULL"]
            continue

        # prepare criteria value to add to query
        envval = AIdb.sanitizeSQL(criteria[crit])
        if AIdb.isRangeCriteria(aisql.getQueue(), crit, AIdb.PROFILES_TABLE):
            # If no default profiles are requested, then we mustn't allow
            # this criteria to be NULL.  It must match the client's given
            # value for this criteria.
            if no_default:
                if crit == "mac":
                    nvpairs += ["(HEX(MIN" + crit + ")<=HEX(X'" + envval + \
                        "'))"]

                    nvpairs += ["(HEX(MAX" + crit + ")>=HEX(X'" + envval + \
                        "'))"]
                else:
                    nvpairs += ["(MIN" + crit + "<='" + envval + "')"]
                    nvpairs += ["(MAX" + crit + ">='" + envval + "')"]
            else:
                if crit == "mac":
                    nvpairs += ["(MIN" + crit + " IS NULL OR "
                        "HEX(MIN" + crit + ")<=HEX(X'" + envval + "'))"]
                    nvpairs += ["(MAX" + crit + " IS NULL OR HEX(MAX" +
                        crit + ")>=HEX(X'" + envval + "'))"]
                else:
                    nvpairs += ["(MIN" + crit + " IS NULL OR MIN" +
                        crit + "<='" + envval + "')"]
                    nvpairs += ["(MAX" + crit + " IS NULL OR MAX" +
                        crit + ">='" + envval + "')"]
        else:
            # If no default profiles are requested, then we mustn't allow
            # this criteria to be NULL.  It must match the client's given
            # value for this criteria.
            #
            # Also, since this is a non-range criteria, the value stored
            # in the DB may be a whitespace separated list of single
            # values.  We use a special user-defined function in the
            # determine if the given criteria is in that textual list.
            if no_default:
                nvpairs += ["(is_in_list('" + crit + "', '" + envval + \
                    "', " + crit + ", 'None') == 1)"]
            else:
                nvpairs += ["(" + crit + " IS NULL OR is_in_list('" + crit + \
                    "', '" + envval + "', " + crit + ", 'None') == 1)"]

    if len(nvpairs) > 0:
        q_str += " AND ".join(nvpairs)

        # issue database query
        logging.info("Profile query: " + q_str)
        query = AIdb.DBrequest(q_str)
        aisql.getQueue().put(query)
        query.waitAns()
        if query.getResponse() is None or len(query.getResponse()) == 0:
            msgtxt = _("No profiles found.")
            client_msg += [msgtxt]
            logging.info(msgtxt)
        else:
            for row in query.getResponse():
                profpath = row['file']
                profname = row['name']
                if profname is None:  # should not happen
                    profname = 'unnamed'
                try:
                    if profpath is None:
                        msgtxt = "Database record error - profile path is " \
                            "empty."
                        client_msg += [msgtxt]
                        logging.error(msgtxt)
                        continue
                    msgtxt = _('Processing profile %s') % profname
                    client_msg += [msgtxt]
                    logging.info(msgtxt)
                    with open(profpath, 'r') as pfp:
                        raw_profile = pfp.read()
                    # do any template variable replacement {{AI_xxx}}
                    tmpl_profile = sc.perform_templating(raw_profile,
                                                         template_dict)
                    # precautionary validation of profile, logging only
                    sc.validate_profile_string(tmpl_profile, image_dir,
                                               dtd_validation=True,
                                               warn_if_dtd_missing=True)
                except IOError as err:
                    msgtxt = _("Error:  I/O error: ") + str(err)
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    continue
                except OSError:
                    msgtxt = _("Error:  OS error on profile ") + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    continue
                except KeyError:
                    msgtxt = _('Error:  could not find criteria to substitute '
                        'in template: ') + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    logging.error('Profile with template substitution error:' +
                            raw_profile)
                    continue
                except lxml.etree.XMLSyntaxError as err:
                    # log validation error and proceed
                    msgtxt = _(
                            'Warning:  syntax error found in profile: ') \
                            + profpath
                    client_msg += [msgtxt]
                    logging.error(msgtxt)
                    for error in err.error_log:
                        msgtxt = _('Error:  ') + error.message
                        client_msg += [msgtxt]
                        logging.error(msgtxt)
                    logging.info([_('Profile failing validation:  ') +
                                 lxml.etree.tostring(root)])
                # build MIME message and attach to outer MIME message
                msg = MIMEText(tmpl_profile, 'xml')
                # indicate in header that this is an attachment
                msg.add_header('Content-Disposition', 'attachment',
                               filename=profname)
                # attach this profile to the manifest and any other profiles
                outermime.attach(msg)
                msgtxt = _('Parsed and loaded profile: ') + profname
                client_msg += [msgtxt]
                logging.info(msgtxt)

    # any profiles and AI manifest have been attached to MIME message
    # specially format list of messages for display on AI client console
    if client_msg:
        outtxt = ''
        for msgtxt in client_msg:
            msgtxt = _('SC profile locator:') + msgtxt
            outtxt += str(msgtxt) + '\n'
        # add AI client console messages as single plain text attachment
        msg = MIMEText(outtxt, 'plain')  # create MIME message
        outermime.attach(msg)  # attach MIME message to response

    print outermime.as_string()  # send MIME-formatted message
Ejemplo n.º 18
0
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()
Ejemplo n.º 19
0
def prepValuesAndRanges(criteriaRoot, database, table=AIdb.MANIFESTS_TABLE):
# =============================================================================
    """
    Processes criteria manifest data already read into memory but before
    it is stored in the AI database.

    Does the following:
    - When a criterion range of one is given by a single <value>, morph
            that value so it can be stored in the database as a <range>.
    - Pad the digit strings of MAC addresses so that the six values between
            the colons are two hex digits each.
    - Pad the digit strings of IP addresses so that the four values between
            the dots are three digits each and between 0 and 255.
    - Strip off colons from MAC addresses and dots from IPv4 addresses.

    Args:
      - criteriaRoot: Tree root for criteria manifest.  This is where
            data is checked / modified.
      - database: Used to find which criteria are range criteria.

    Returns: Nothing.  However, data may be checked and modified per above.

    Raises:
    - Exception: ValueError - a range criteria provided as a list of values
                            - a non-range criteria provided as a range
    - Exceptions raised by database calls, and calls to
            - checkIPv4()
            - checkMAC()
    """
# =============================================================================

    # Find from the database which criteria are range criteria.
    # Range criteria named xxx have names bounds values MINxxx and MAXxxx.
    # Assume that MINxxx is a good enough check.
    # All criteria names in database are stored as lower case, except
    # for their "MIN" and "MAX" prefixes.
    range_crit = list()
    for crit_name in AIdb.getCriteria(database.getQueue(), table,
        onlyUsed=False, strip=False):
        if (crit_name.startswith("MIN")):
            range_crit.append(crit_name.replace("MIN", "", 1))

    # Loop through all criteria elements.
    for crit in criteriaRoot.findall('.//ai_criteria'):
        crit_name = crit.attrib['name'].lower()
        val_range = crit.getchildren()[0]

        # <range>'s here are a single element with a single
        # string containing two space-separated values for MIN and MAX
        # <value>'s here are a single element with a single
        # string containing one value or a space-separated list of values.
        value_list = val_range.text.split()
        num_values = len(value_list)

        # val_range.tag will be either 'value' or 'range'.
        # This is syntactically validated by the schema.
        if val_range.tag == "value":
            # Allow a list of values to be provided with the 'value' tag.
            # However, for criteria that can be provided as a range, we
            # currently do not support lists for them.
            if num_values > 1 and crit_name in range_crit:
                raise ValueError("Criteria '" + crit_name + "' is not "
                    "supported to be provided as a list of values")
        else:
            # For ranges, make sure it is indeed a range criteria
            if crit_name not in range_crit:
                raise ValueError("Criteria '" + crit_name + "' can not "
                    "be passed as a range pair")

        # For value criteria, there is no need to do anything to store
        # single value into val_range.text.  It is already there.
        # Just check architecture and cpu values because it is common to
        # confuse i86pc and i386 or sparc and sun4v
        if crit_name == "arch":
            for one_value in value_list:
                try:
                    checkArch(one_value)
                except ValueError as err:
                    # Just print warning
                    print >> sys.stderr, err
        elif crit_name == "cpu":
            for one_value in value_list:
                try:
                    checkCPU(one_value)
                except ValueError as err:
                    # Just print warning
                    print >> sys.stderr, err
        #
        # For some types supported by range criteria, some additional
        # format checking is needed.  Also, range criteria that are passed
        # as single values need to be split into a range where min=max.

        # Current criterion is a range criterion.
        if range_crit.count(crit_name) > 0:

            # Each value will have already been checked against the
            # schema.  IPv4 values will be 4 numbers ranging from
            # 0-255, separated by dots.  MAC values will be 6 hex
            # numbers ranging from 0-FF, separated by colons.
            # There may be one or two values.

            new_values = ""
            for one_value in value_list:

                # Space between (range) values.
                if new_values != "":
                    new_values += " "

                # Handle "unbounded" keyword; and pass lowercase
                lowered_value = one_value.lower()
                if lowered_value == "unbounded":
                    new_values += lowered_value

                # Handle IPv4 addressses.
                elif crit_name == "ipv4" or crit_name == "network":
                    new_values += checkIPv4(one_value)

                # Handle MAC addresses.
                elif crit_name == "mac":
                    new_values += checkMAC(one_value)

                # Handle everything else by passing through.
                else:
                    new_values += one_value

                # Single values which come in under a "value"
                # tag but represent a range (e.g. a single ipv4
                # value) are "converted" into the form a range
                # value pair would take (a single string
                # consisting of two items) where
                # the min value = max value.
                if val_range.tag == "value":
                    # Change to a range.
                    # Set min = max = value.
                    val_range.tag = "range"
                    val_range.text = \
                        new_values + " " + new_values
                elif val_range.tag == "range":
                    # values will be a list of 2 already.
                    val_range.text = new_values
Ejemplo n.º 20
0
def sql_values_from_criteria(criteria, queue, table, gbl=False):
    ''' Given a criteria dictionary, for the indicated DB table
    and queue, return a tuple composed of lists whose elements can be used
    to construct SQLite clauses.  If gbl is true, build a clause that
    will affect all database records if criteria is missing - a global effect.
    Args:
        criteria - criteria dictionary
        queue - database queue
        table - database table
        gbl - if True, global
    Returns: a tuple for SQLite clauses respectively: WHERE, INTO, VALUES
    '''
    where = list()  # for WHERE clause
    intol = list()  # for INTO clause
    vals = list()  # for VALUES clause
    for crit in AIdb.getCriteria(queue, table, onlyUsed=False, strip=True):

        # Determine if this crit is a range criteria or not.
        is_range_crit = AIdb.isRangeCriteria(queue, crit, table)

        # Get the value from the manifest
        values = criteria[crit]
        # the critera manifest didn't specify this criteria
        if values is None:
            # if the criteria we're processing is a range criteria, fill in
            # NULL for two columns, MINcrit and MAXcrit
            vals += ["NULL"]
            if is_range_crit:
                where += ["MIN" + crit + " IS NULL"]
                where += ["MAX" + crit + " IS NULL"]
                intol += ["MIN" + crit]
                intol += ["MAX" + crit]
                vals += ["NULL"]
            # this is a single value
            else:
                where += [crit + " IS NULL"]
                intol += [crit]
        # This is a value criteria (not a range).  'values' is a list
        # with one or more items.
        elif not is_range_crit:
            intol += [crit]
            val = AIdb.format_value(crit, " ".join(values))
            where += [crit + "=" + val]
            vals += [val]
        # Else this is a range criteria.  'values' is a two-item list
        else:
            # Set the MIN column for this range criteria
            if values[0] == 'unbounded':
                if not gbl:
                    where += ["MIN" + crit + " IS NULL"]
                    intol += ["MIN" + crit]
                    vals += ['NULL']
            else:
                intol += ["MIN" + crit]
                if crit == 'mac':
                    val = AIdb.format_value(crit, verifyXML.checkMAC(
                        values[0])).upper()
                    where += ["HEX(MIN" + crit + ")<=HEX(" + val + ")"]
                else:
                    val = AIdb.format_value(crit, values[0]).lower()
                    where += ["MIN" + crit + "<=" + val]
                vals += [val]
            # Set the MAX column for this range criteria
            if values[1] == 'unbounded':
                if not gbl:
                    where += ["MAX" + crit + " IS NULL"]
                    intol += ["MAX" + crit]
                    vals += ['NULL']
            else:
                intol += ["MAX" + crit]
                if crit == 'mac':
                    val = AIdb.format_value(crit, verifyXML.checkMAC(
                        values[1])).upper()
                    where += ["HEX(MAX" + crit + ")>=HEX(" + val + ")"]
                else:
                    val = AIdb.format_value(crit, values[1]).lower()
                    where += ["MAX" + crit + ">=" + val]
                vals += [val]
    return where, intol, vals
Ejemplo n.º 21
0
    def manifest_xml(self, postData=None):
        """
        This is manifest.xml the special object to list needed criteria
        or return a manifest given a set of criteria
        """
        if postData is not None:
            criteria = {}

            # process each key/value pair of the POST data
            while postData:
                try:
                    [key_value, postData] = postData.split(';', 1)
                except (ValueError, NameError, TypeError, KeyError):
                    key_value = postData
                    postData = ''
                try:
                    [key, value] = key_value.split('=', 1)
                    criteria[key] = value
                except (ValueError, NameError, TypeError, KeyError):
                    criteria = {}
            manifest = AIdb.findManifest(criteria, self.AISQL)
            # check if findManifest() returned a number and one larger than 0
            # (means we got multiple manifests back -- an error)
            if str(manifest).isdigit() and manifest > 0:
                web_page = \
                    E.HTML(
                           E.HEAD(
                                  E.TITLE(_("Error!"))
                           ),
                           E.BODY(
                                  E.P(_("Criteria indeterminate -- this "
                                        "should not happen! Got %s matches.") %
                                      str(manifest))
                           )
                    )
                return lxml.etree.tostring(web_page, pretty_print=True)

            # check if findManifest() returned a number equal to 0
            # (means we got no manifests back -- thus we serve the default)
            elif manifest == 0:
                manifest = "default.xml"

            # else findManifest() returned the name of the manifest to serve
            # (or it is now set to default.xml)
            try:
                return serve_file(os.path.abspath(
                        os.path.join(self.base_dir, os.path.join("AI_data",
                        manifest))), "application/x-download", "attachment")
            except OSError:
                raise cherrypy.NotFound("/manifests/" + str(manifest))

        # this URI is not being requested using a POST method
        # return criteria list for AI-client to know what needs querried
        else:
            # <CriteriaList>
            #       <Version Number="0.5">
            #       <Criteria Name="MEM">
            #       <Criteria Name="arch">
            # ...
            # </CriteriaList>

            cherrypy.response.headers['Content-Type'] = "text/xml"
            XML = lxml.etree.Element("CriteriaList")
            version_value = lxml.etree.Element("Version")
            version_value.attrib["Number"] = "0.5"
            XML.append(version_value)
            for crit in AIdb.getCriteria(self.AISQL.getQueue(), strip=True):
                tag = lxml.etree.Element("Criteria")
                tag.attrib["Name"] = crit
                XML.append(tag)
            return lxml.etree.tostring(XML, pretty_print=True)
Ejemplo n.º 22
0
    def index(self):
        """ The server's main page """

        # generate the list of criteria for the criteria table header
        criteriaHeader = E.TR()
        for crit in AIdb.getCriteria(self.AISQL.getQueue(), strip=False):
            criteriaHeader.append(E.TH(crit))

        # generate the manifest rows for the criteria table body
        names = AIdb.getManNames(self.AISQL.getQueue())
        tableBody = E.TR()
        for manifest in names:

            # iterate through each manifest (and instance)
            for instance in range(0,
                    AIdb.numInstances(manifest, self.AISQL.getQueue())):

                tableBody.append(E.TR())
                # print the manifest name only once (key off instance 0)
                if instance == 0:
                    tableBody.append(
                        E.TD(E.A(manifest,
                                 href="/manifests/" + manifest,
                                 rowspan=str(AIdb.numInstances(manifest,
                                    self.AISQL.getQueue())))
                            )
                    )
                else:
                    tableBody.append(E.TD())
                critPairs = AIdb.getManifestCriteria(manifest, instance,
                                                     self.AISQL.getQueue(),
                                                     onlyUsed=True,
                                                     humanOutput=True)
                # critPairs is an SQLite3 row object which doesn't support
                # iteritems(), etc.
                for crit in critPairs.keys():
                    formatted_val = AIdb.formatValue(crit, critPairs[crit])
                    # if we do not get back a valid value ensure a hyphen is
                    # printed (this prevents "" from printing)
                    if formatted_val and critPairs[crit]:
                        tableBody.append(E.TD(formatted_val, align="center"))
                    else:
                        tableBody.append(E.TD(lxml.etree.Entity("nbsp"),
                                              align="center"))

        # print the default manifest at the end of the table
        else:
            tableBody.append(
                             E.TR(
                                  E.TD(
                                       E.A("Default",
                                           href="/manifests/default.xml")),
                                  E.TD(lxml.etree.Entity("nbsp"),
                                       colspan=str(max(len(list(
                                       AIdb.getCriteria(self.AISQL.getQueue(),
                                       strip=False))), 1)),
                                       align="center")
                             )
            )
        web_page = \
                E.HTML(
                       E.HEAD(
                              E.TITLE(_("%s A/I Webserver") % _DISTRIBUTION)
                       ),
                       E.BODY(
                              E.H1(_("Welcome to the %s A/I "
                                     "webserver!") % _DISTRIBUTION),
                              E.P(_("This server has the following "
                                    "manifests available, served to clients "
                                    "matching required criteria.")),
                              E.TABLE(
                                      E.TR(
                                           E.TH(_("Manifest"), rowspan="2"),
                                           E.TH(_("Criteria List"),
                                                colspan=str(max(len(list(
                                                AIdb.getCriteria(self.AISQL.\
                                                getQueue(),
                                                strip=False))), 1)))
                                      ),
                                      criteriaHeader,
                                      tableBody,
                                      border="1", align="center"
                              ),
                       )
                )
        return lxml.etree.tostring(web_page, pretty_print=True)
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]

        # Determine if this crit is a range criteria or not.
        is_range_crit = AIdb.isRangeCriteria(db.getQueue(), crit,
            AIdb.MANIFESTS_TABLE)

        # Process "value" criteria here (check if the criteria exists in
        # DB, and then find collisions)
        if not is_range_crit:
            # 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 a value in the list of values to be added is equal
                # to a value in the list of values for this criteria for this
                # row
                for value in man_criterion:
                    if AIdb.is_in_list(crit, value, str(row[Fields.CRIT]),
                        None):
                        # 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:
            # Clean-up NULL's and change "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 for a properly ordered range (with unbounded being 0 or
            # Inf.) but ensure both are not unbounded.
            # Check for:
            #       a range of zero to inf -- not a valid range
            #  and
            #       min > max -- range order reversed
            #
            if (man_criterion[0] == 0 and man_criterion[1] == long(INFINITY)):
                raise SystemExit(_("Error:\tCriteria %s is not a valid range, "
                                   "MIN and MAX unbounded.") % crit)

            if ((man_criterion[0] != 0 and
                 man_criterion[1] != long(INFINITY)) and
                (long(man_criterion[0]) > long(man_criterion[1]))):
                raise SystemExit(_("Error:\tCriteria %s is not a valid range, "
                                   "MIN > MAX.") % crit)
            # check to see that this criteria exists in the database columns
            man_crit = AIdb.getCriteria(db.getQueue(), onlyUsed=False,
                                        strip=False)
            if 'MIN' + crit not in man_crit and 'MAX' + crit not in man_crit:
                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
Ejemplo n.º 24
0
def list_manifests(service):
    '''Replies to the client with criteria list for a service.
       The output should be similar to installadm list.

    Args
        service - the name of the service being listed

    Returns
        None

    Raises
        None
    '''
    print 'Content-Type: text/html'  # HTML is following
    print  # blank line, end of headers
    print '<html>'
    print '<head>'
    sys.stdout.write('<title>%s %s</title>' %
                     (_('Manifest list for'), service))
    print '</head><body>'

    port = 0
    try:
        smf.AISCF(FMRI="system/install/server")
    except KeyError:
        # report the internal error to error_log and requesting client
        sys.stderr.write(
            _("error:The system does not have the "
              "system/install/server SMF service."))
        sys.stdout.write(
            _("error:The system does not have the "
              "system/install/server SMF service."))
        return
    services = config.get_all_service_names()
    if not services:
        # report the error to the requesting client only
        sys.stdout.write(_('error:no services on this server.\n'))
        return

    found = False
    if config.is_service(service):
        service_ctrl = AIService(service)
        found = True

        # assume new service setup
        path = service_ctrl.database_path
        if os.path.exists(path):
            try:
                aisql = AIdb.DB(path)
                aisql.verifyDBStructure()
            except StandardError as err:
                # report the internal error to error_log and
                # requesting client
                sys.stderr.write(
                    _('error:AI database access '
                      'error\n%s\n') % err)
                sys.stdout.write(
                    _('error:AI database access '
                      'error\n%s\n') % err)
                return

            # generate the list of criteria for the criteria table header
            criteria_header = E.TR()
            for crit in AIdb.getCriteria(aisql.getQueue(), strip=False):
                criteria_header.append(E.TH(crit))

            # generate the manifest rows for the criteria table body
            names = AIdb.getManNames(aisql.getQueue())
            table_body = E.TR()
            allcrit = AIdb.getCriteria(aisql.getQueue(), strip=False)
            colspan = str(max(len(list(allcrit)), 1))
            for manifest in names:

                # iterate through each manifest (and instance)
                for instance in range(
                        0, AIdb.numInstances(manifest, aisql.getQueue())):

                    table_body.append(E.TR())
                    # print the manifest name only once (from instance 0)
                    if instance == 0:
                        href = '../' + service + '/' + manifest
                        row = str(AIdb.numInstances(manifest,
                                                    aisql.getQueue()))
                        table_body.append(
                            E.TD(E.A(manifest, href=href, rowspan=row)))
                    else:
                        table_body.append(E.TD())

                    crit_pairs = AIdb.getManifestCriteria(manifest,
                                                          instance,
                                                          aisql.getQueue(),
                                                          onlyUsed=True,
                                                          humanOutput=True)

                    # crit_pairs is an SQLite3 row object which doesn't
                    # support iteritems(), etc.
                    for crit in crit_pairs.keys():
                        formatted_val = AIdb.formatValue(
                            crit, crit_pairs[crit])
                        # if we do not get back a valid value ensure a
                        # hyphen is printed (prevents "" from printing)
                        if formatted_val and crit_pairs[crit]:
                            table_body.append(
                                E.TD(formatted_val, align="center"))
                        else:
                            table_body.append(
                                E.TD(lxml.etree.Entity("nbsp"),
                                     align="center"))

            # print the default manifest at the end of the table,
            # which has the same colspan as the Criteria List label
            else:
                href = '../' + service + '/default.xml'
                table_body.append(
                    E.TR(
                        E.TD(E.A("Default", href=href)),
                        E.TD(lxml.etree.Entity("nbsp"),
                             colspan=colspan,
                             align="center")))
            web_page = E.HTML(
                E.HEAD(E.TITLE(_("OmniOS Automated "
                                 "Installation Webserver"))),
                E.BODY(
                    E.H1(
                        _("Welcome to the OmniOS "
                          "Automated Installation webserver!")),
                    E.P(
                        _("Service '%s' has the following "
                          "manifests available, served to clients "
                          "matching required criteria.") % service),
                    E.TABLE(E.TR(E.TH(_("Manifest"), rowspan="2"),
                                 E.TH(_("Criteria List"), colspan=colspan)),
                            criteria_header,
                            table_body,
                            border="1",
                            align="center"),
                ))
            print lxml.etree.tostring(web_page, pretty_print=True)

    # service is not found, provide available services on host
    if not found:
        sys.stdout.write(_('Service <i>%s</i> not found.  ') % service)
        sys.stdout.write(_('Available services are:<p><ol><i>'))
        host = socket.gethostname()
        for service_name in config.get_all_service_names():
            # assume new service setup
            port = config.get_service_port(service_name)
            sys.stdout.write(
                '<a href="http://%s:%d/cgi-bin/'
                'cgi_get_manifest.py?version=%s&service=%s">%s</a><br>\n' %
                (host, port, VERSION, service_name, service_name))
        sys.stdout.write('</i></ol>%s' % _('Please select a service '
                                           'from the above list.'))

    print '</body></html>'
def validate_criteria_from_user(criteria, dbo, table):
    ''' Validate profile criteria from dictionary containing command line input
    Args:    criteria - Criteria object holding the criteria that is to be
                        added/set for a manifest.
             dbo - AI_database object for the install service.
             table - name of database table
    Raises:  SystemExit if:
        - criteria is not found in database
        - value is not valid for type (integer and hexadecimal checks)
        - range is improper
    Returns: nothing
    '''
    # find all possible profile criteria expressed as DB table columns
    critlist = []
    # take criteria from generator
    for crit in AIdb.getCriteria(dbo.getQueue(), table, onlyUsed=False,
                                 strip=False):
        critlist.append(crit)
    # verify each range criteria is well formed
    for crit in criteria:
        # gather this criteria's values
        man_criterion = criteria[crit]
        # check "value" criteria here (check the criteria exists in DB
        if not AIdb.isRangeCriteria(dbo.getQueue(), crit, table):
            # only check criteria in use in the DB
            if crit not in critlist:
                raise SystemExit(_(
                    "Error:\tCriteria %s is not a valid criteria!") % 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
        else:
            # check for a properly ordered range (with unbounded being 0 or
            # Inf.)
            if man_criterion[0] != "unbounded" and \
                man_criterion[1] != "unbounded" and \
                man_criterion[0] > man_criterion[1]:  # Check min > max
                raise SystemExit(_(
                    "Error:\tCriteria %s is not a valid range (MIN > MAX) ")
                    % crit)
            # Clean-up NULL's and changed "unbounded"s to 0 and
            # the maximum integer value
            # Note "unbounded"s are already converted to lower case during
            # input processing
            if man_criterion[0] == "unbounded":
                man_criterion[0] = "0"
            if man_criterion[1] == "unbounded":
                man_criterion[1] = str(sys.maxint)
            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 critlist and 'MAX' + crit not in critlist:
                raise SystemExit(_(
                    "Error:\tCriteria %s is not a valid criteria!") % crit)