facet.py revision 2931
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#
# basic facet support
import fnmatch
import re
import types
# store information on facets; subclass dict
# and maintain ordered list of keys sorted
# by length.
# subclass __getitem__ so that queries w/
# actual facets find match
#
# For image planning purposes and to be able to compare facet objects
# deterministically, facets must be sorted. They are first sorted by
# source (more details below), then by length, then lexically.
#
# Facets can come from three different sources.
#
# SYSTEM facets are facets whose values are assigned by the system.
# These are usually facets defined in packages which are not set in an
# image, and the value assigned by the system is always true. These
# facets will usually never be found in a Facets dictionary. (Facets
# dictionaries only contain facets which are explicitly set.)
#
# LOCAL facets are facets which have been set locally in an image
# using pkg(1) or the pkg api. Explicitly set LOCAL facets are stored
# in Facets.__local. Facets which are not explicitly set but match an
# explicitly set LOCAL facet glob pattern are also considered to be
# LOCAL.
#
# PARENT facets are facets which are inherited from a parent image.
# they are managed internally by the packaging subsystem. Explicitly
# inherited facets are stored in Facets.__inherited. Facets which are
# not explicitly set but match an explicitly set PARENT facet glob
# pattern are also considered to be PARENT.
#
# When evaluating facets, all PARENT facets are evaluated before LOCAL
# facets. This is done by ensuring that all PARENT facets come before
# any LOCAL facets in __keylist. This is done because PARENT facets
# exist to propagate faceted dependencies between linked images, which
# is needed to ensure the solver can run successfully. ie, if a
# parent image relaxes dependencies via facet version-locks, then the
# child needs to inherit those facets since otherwise it is more
# constrained in possible solutions than it's parent and likely won't
# be able to plan an update that keeps it in sync with it's parent.
#
# Sine PARENT facets take priority over LOCAL facets, it's possible to
# have conflicts between the two. In the case where a facet is both
# inherited and set locally, both values are preserved, but the
# inherited value masks the local value. Users can list and update
# local values while they are masked using pkg(1), but as long as the
# values are masked they will not affect image planning operations.
# Once an inherited facet that masks a local facet is removed, the
# local facet will be restored.
#
FACET_SRC_SYSTEM = "system"
FACET_SRC_LOCAL = "local"
FACET_SRC_PARENT = "parent"
self.__local_ro = None
self.__inherited = {}
self.__inherited_ro = None
# initialize ourselves
"""Returns the serialized state of this object in a format
that that can be easily stored using JSON, pickle, etc."""
return [
[k, v, True]
] + [
[k, v, False]
]
"""Update the state of this object using previously serialized
state obtained via getstate()."""
for k, v, inhertited in state:
if not inhertited:
rv[k] = v
else:
rv._set_inherited(k, v)
return rv
"""Compare the facet match priority of two Facets objects.
Since the match priority of a Facets object is dependent upon
facet sources (local vs parent) and names, we're essentially
ensuring that both objects have the same set of facet sources
and names."""
"""Compare the facet values of two Facets objects. This
comparison ignores any masked values."""
"""Compare all the facet values of two Facets objects. This
comparison takes masked values into account."""
if rv == 0:
return rv
"""Compare two Facets objects. This comparison takes masked
values into account."""
# check if we're getting compared against something other than
# another Factes object.
return 1
# Check for effective facet value changes that could affect
# solver computations.
if rv != 0:
return rv
# Check for facet priority changes that could affect solver
# computations. (Priority changes can occur when local or
# inherited facets are added or removed.)
if rv != 0:
return rv
# There are no outwardly visible facet priority or value
# changes that could affect solver computations, but it's
# still possible that we're changing the set of local or
# inherited facets in a way that doesn't affect solver
# computations. For example: we could be adding a local
# facet with a value that is masked by an inherited facet, or
# having a facet transition from being inherited to being
# local without a priority or value change. Check if this is
# the case.
return rv
"""redefine in terms of __cmp__()"""
"""redefine in terms of __cmp__()"""
"""redefine in terms of __cmp__()"""
"""redefine in terms of __cmp__()"""
"""redefine in terms of __cmp__()"""
"""redefine in terms of __cmp__()"""
s = "<"
s += ", ".join([
])
s += ">"
return s
def __keylist_sort(self):
"""Update __keysort, which is used to determine facet matching
order. Inherited facets always take priority over local
facets so make sure all inherited facets come before local
facets in __keylist. All facets from a given source are
sorted by length, and facets of equal length are sorted
lexically."""
def facet_sort(x, y):
if i != 0:
return i
return cmp(x, y)
i
for i in self
if i in self.__inherited
], cmp=facet_sort)
i
for i in self
if i not in self.__inherited
], cmp=facet_sort)
raise KeyError, 'key must start with "facet".'
raise ValueError, "value must be boolean"
# save the facet in the local or inherited dictionary
# clear the corresponding read-only dictionary
if inherited:
self.__inherited_ro = None
else:
self.__local_ro = None
# Inherited facets always take priority over local facets.
if keylist_sort:
"""__setitem__ only operates on local facets."""
"""Implement facet lookup algorithm here
Note that _allow_facet bypasses __getitem__ for performance
reasons; if __getitem__ changes, _allow_facet in _varcet.c
must also be updated.
We return a tuple of the form (<key>, <value>) where key is
the explicitly set facet name (which may be a glob pattern)
that matched the caller specific facet name."""
raise KeyError, "key must start w/ facet."
return None, True # be inclusive
# check for an attempt to delete an invalid facet
# check for an attempt to delete an invalid local facet
# we should never try to delete an invalid inherited facet
# the inherited value was overriding a local value
# that should now be exposed
else:
# delete the item
# delete item from the local or inherited dictionary
# clear the corresponding read-only dictionary
if inherited:
self.__inherited_ro = None
else:
self.__local_ro = None
if keylist_sort:
return rv
"""__delitem__ only operates on local facets."""
# allow_action is provided as a native function (see end of class
# declaration).
"""Set an inherited facet."""
def _clear_inherited(self):
"""Clear all inherited facet."""
facets present on an action."""
# find all the facets present in the current action
a
if a.startswith("facet.")
])
if facet in action_facets:
# we found a matching facet.
continue
for action_facet in action_facets:
# we found a matching facet.
break
"""pop() only operates on local facets."""
"default" not in kwargs)
# check if the user specified a default value
if args:
return args[0]
elif "default" in kwargs:
return kwargs["default"]
raise KeyError, 'pop(): dictionary is empty'
"""popitem() only operates on local facets."""
item = None
break
if item is None:
raise KeyError, 'popitem(): dictionary is empty'
# preserve inherited facets.
for k, v in d.__inherited.iteritems():
self._set_inherited(k, v)
self[k] = v
return
for k in d:
self[k] = d[k]
"""A facet may be set via multiple sources and hence have
multiple values. If there are multiple values for a facet,
all but one of those values will be masked. So for a given
facet, return a list of tuples of the form (<value>, <src>,
<masked>) which represent all currently set values for this
facet."""
rv = []
return rv
yield k, self[k]
self.__local_ro = None
self.__inherited = {}
self.__inherited_ro = None
"""Report the source of a facet value if we were to attempt to
look it up in the current Facets object dictionary."""
if k in self.__inherited:
return self.FACET_SRC_PARENT
return self.FACET_SRC_LOCAL
assert k is None and k not in self
return self.FACET_SRC_SYSTEM
# For convenience, provide callers with direct access to local and
# parent facets via cached read-only dictionaries.
if self.__local_ro is None:
return self.__local_ro
if self.__inherited_ro is None:
return self.__inherited_ro