def test_no_match_case_sensitive(self):
     ''' Test that we don't match a value on the wrong case.'''
     value = "FoO"
     value_list = "bar foo blah"
     self.assertFalse(AIdb.is_in_list('zonename', value, value_list, None))
 def test_match_list_not_first_value(self):
     ''' Test that we match a value in the middle of the list '''
     value = "foo"
     value_list = "bar foo bum"
     self.assertTrue(AIdb.is_in_list('dummy_crit', value, value_list, None))
 def test_match_case_sensitive(self):
     ''' Test that we match a value based on a case senstive criteria '''
     value = "FoO"
     value_list = "bar FoO blah"
     self.assertTrue(AIdb.is_in_list('zonename', value, value_list, None))
Example #4
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 test_match_list_first_value(self):
     ''' Test that we match a value that is the first in the list '''
     value = "foo"
     value_list = "foo bar bum"
     self.assertTrue(AIdb.is_in_list('dummy_crit', value, value_list, None))
 def test_no_match_case_sensitive(self):
     ''' Test that we don't match a value on the wrong case.'''
     value = "FoO"
     value_list = "bar foo blah"
     self.assertFalse(AIdb.is_in_list('zonename', value, value_list, None))
Example #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
 def test_match_case_sensitive(self):
     ''' Test that we match a value based on a case senstive criteria '''
     value = "FoO"
     value_list = "bar FoO blah"
     self.assertTrue(AIdb.is_in_list('zonename', value, value_list, None))
 def test_match_list_not_first_value(self):
     ''' Test that we match a value in the middle of the list '''
     value = "foo"
     value_list = "bar foo bum"
     self.assertTrue(AIdb.is_in_list('dummy_crit', value, value_list, None))
 def test_match_list_first_value(self):
     ''' Test that we match a value that is the first in the list '''
     value = "foo"
     value_list = "foo bar bum"
     self.assertTrue(AIdb.is_in_list('dummy_crit', value, value_list, None))
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 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