This file is indexed.

/usr/share/pyshared/juju/machine/constraints.py is in juju-0.7 0.7-0ubuntu2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
import operator
from UserDict import DictMixin

from juju.errors import ConstraintError, UnknownConstraintError


def _dont_convert(s):
    return s


class _ConstraintType(object):
    """Defines a constraint.

    :param str name: The constraint's name
    :param default: The default value as a str, or None to indicate "unset"
    :param converter: Function to convert str value to "real" value (and
        thereby implicitly validate it; should raise ValueError)
    :param comparer: Function used to determine whether one constraint
        satisfies another
    :param bool visible: If False, indicates a computed constraint which
        should not be settable by a user.

    Merely creating a Constraint does not activate it; you also need to
    register it with a specific ConstraintSet.
    """

    def __init__(self, name, default, converter, comparer, visible):
        self.name = name
        self.default = default
        self._converter = converter
        self._comparer = comparer
        self.visible = visible

    def convert(self, s):
        """Convert a string representation of a constraint into a useful form.
        """
        if s is None:
            return
        try:
            return self._converter(s)
        except ValueError as e:
            raise ConstraintError(
                "Bad %r constraint %r: %s" % (self.name, s, e))

    def can_satisfy(self, candidate, benchmark):
        """Check whether candidate can satisfy benchmark"""
        return self._comparer(candidate, benchmark)


class ConstraintSet(object):
    """A ConstraintSet represents all constraints applicable to a provider.

    Individual providers can construct ConstraintSets which will be used to
    construct Constraints objects directly relevant to that provider."""

    def __init__(self, provider_type):
        self._provider_type = provider_type
        self._registry = {}
        self._conflicts = {}

        # These constraints must always be available (but are not user-visible
        # or -settable).
        self.register("ubuntu-series", visible=False)
        self.register("provider-type", visible=False)

    def register(self, name, default=None, converter=_dont_convert,
                 comparer=operator.eq, visible=True):
        """Register a constraint to be handled by this ConstraintSet.

        :param str name: The constraint's name
        :param default: The default value as a str, or None to indicate "unset"
        :param converter: Function to convert str value to "real" value (and
            thereby implicitly validate it; should raise ValueError)
        :param comparer: Function used to determine whether one constraint
            satisfies another
        :param bool visible: If False, indicates a computed constraint which
            should not be settable by a user.
        """
        self._registry[name] = _ConstraintType(
            name, default, converter, comparer, visible)
        self._conflicts[name] = set()

    def register_conflicts(self, reds, blues):
        """Set cross-constraint override behaviour.

        :param reds: list of constraint names which affect all constraints
            specified in `blues`
        :param blues: list of constraint names which affect all constraints
            specified in `reds`

        When two constraints conflict:

        * It is an error to set both constraints in the same Constraints.
        * When a Constraints overrides another which specifies a conflicting
          constraint, the value in the overridden Constraints is cleared.
        """
        for red in reds:
            self._conflicts[red].update(blues)
        for blue in blues:
            self._conflicts[blue].update(reds)

    def register_generics(self, instance_type_names):
        """Register a common set of constraints.

        This always includes arch, cpu, and mem; and will include instance-type
        if instance_type_names is not empty. This is because we believe
        instance-type to be a broadly applicable concept, even though the only
        provider that registers names here (and hence accepts the constraint)
        is currently EC2.
        """
        self.register("arch", default="amd64", converter=_convert_arch)
        self.register(
            "cpu", default="1", converter=_convert_cpu, comparer=operator.ge)
        self.register(
            "mem", default="512M", converter=_convert_mem,
            comparer=operator.ge)

        if instance_type_names:

            def convert(instance_type_name):
                if instance_type_name in instance_type_names:
                    return instance_type_name
                raise ValueError("unknown instance type")

            self.register("instance-type", converter=convert)
            self.register_conflicts(["cpu", "mem"], ["instance-type"])

    def names(self):
        """Get the names of all registered constraints."""
        return self._registry.keys()

    def get(self, name):
        """Get the (internal) _ConstraintType object corresponding to `name`.

        Returns None if no _ConstraintType has been registered under that name.
        """
        return self._registry.get(name)

    def parse(self, strs):
        """Create a Constraints from strings (as used on the command line)"""
        data = {"provider-type": self._provider_type}
        for s in strs:
            try:
                name, value = s.split("=", 1)
                constraint = self.get(name)
                if constraint is None:
                    raise UnknownConstraintError(name)
                if value == "any":
                    value = None
                if value == "":
                    value = constraint.default
                constraint.convert(value)
            except ValueError as e:
                raise ConstraintError(
                    "Could not interpret %r constraint: %s" % (s, e))
            if not constraint.visible:
                raise ConstraintError(
                    "Cannot set computed constraint: %r" % name)
            data[name] = value

        conflicts = set()
        for name in sorted(data):
            if data[name] is None:
                continue
            for conflict in sorted(self._conflicts[name]):
                if conflict in data:
                    raise ConstraintError(
                        "Ambiguous constraints: %r overlaps with %r"
                        % (name, conflict))
                conflicts.add(conflict)

        data.update(dict((conflict, None) for conflict in conflicts))
        return Constraints(self, data)

    def load(self, data):
        """Convert a data dict to a Constraints"""
        for k, v in data.items():
            constraint = self.get(k)
            if constraint is not None:
                # Include all of data; validate those parts we know how to.
                constraint.convert(v)
        return Constraints(self, data)


class Constraints(object, DictMixin):
    """A Constraints object encapsulates a set of machine constraints.

    Constraints instances should not be constructed directly; please use
    ConstraintSet's parse and load methods instead.

    They implement a dict interface, which exposes all constraints for the
    appropriate provider, and is the expected mode of usage for clients not
    concerned with the construction or comparison of Constraints objects.

    A Constraints object only ever contains a single "layer" of data, but can
    be combined with other Constraints objects in such a way as to produce a
    single object following the rules laid down in internals/placement-spec.

    Constraints objects can be compared, in a limited sense, by using the
    `can_satisfy` method.
    """

    def __init__(self, available, data):
        self._available = available
        self._data = data

    def keys(self):
        """DictMixin"""
        return self._available.names()

    def __getitem__(self, name):
        """DictMixin"""
        if name not in self.keys():
            raise KeyError(name)
        constraint = self._available.get(name)
        raw_value = self.data.get(name, constraint.default)
        return constraint.convert(raw_value)

    def with_series(self, series):
        """Return a Constraints with the "ubuntu-series" set to `series`"""
        data = dict(self._data)
        data["ubuntu-series"] = series
        return self._available.load(data)

    @property
    def complete(self):
        """Have provider-type and ubuntu-series both been set?"""
        return None not in (
            self.get("provider-type"), self.get("ubuntu-series"))

    @property
    def data(self):
        """Return a dict suitable for serialisation and reconstruction.

        Note that data contains (1) the specified value for every
        constraint that has been explicitly set, and (2) a None value for
        every constraint which conflicts with one that has been set.

        Therefore, by updating one Constraints's data with another's,
        any setting thus masked on the lower level will be preserved as None;
        consequently, Constraints~s can be collapsed onto one another without
        losing any information that is not overridden (whether implicitly or
        explicitly) by the overriding Constraints.
        """
        return dict(self._data)

    def update(self, other):
        """Overwrite `self`'s data from `other`."""
        self._data.update(other.data)

    def can_satisfy(self, other):
        """Can a machine with constraints `self` be used for a unit with
        constraints `other`? ie ::

            if machine_constraints.can_satisfy(unit_constraints):
                # place unit on machine
        """
        if not (self.complete and other.complete):
            # Incomplete constraints cannot satisfy or be satisfied; we should
            # only ever hit this branch if we're running new code (that knows
            # about constraints) against an old deployment (which will contain
            # at least *some* services/machines which don't have constraints).
            return False

        for (name, unit_value) in other.items():
            if unit_value is None:
                # The unit doesn't care; any machine value will be fine.
                continue
            machine_value = self[name]
            if machine_value is None:
                # The unit *does* care, and the machine value isn't
                # specified, so we can't guarantee a match. If we were
                # to update machine constraints after provisioning (ie
                # when we knew the values of the constraints left
                # unspecified) we'd hit this branch less often.  We
                # may also need to do something clever here to get
                # sensible machine reuse on ec2 -- in what
                # circumstances, if ever, is it OK to place a unit
                # specced for one instance-type on a machine of
                # another type? Does it matter if either or both were
                # derived from generic constraints? What about cost?
                return False
            constraint = self._available.get(name)
            if not constraint.can_satisfy(machine_value, unit_value):
                # The machine's value is definitely not ok for the unit.
                return False

        return True


#==============================================================================
# Generic constraint information (used by multiple providers).
_VALID_ARCHS = ("i386", "amd64", "arm", "arm64")
_MEGABYTES = 1
_GIGABYTES = _MEGABYTES * 1024
_TERABYTES = _GIGABYTES * 1024
_MEM_SUFFIXES = {"M": _MEGABYTES, "G": _GIGABYTES, "T": _TERABYTES}


def _convert_arch(s):
    if s in _VALID_ARCHS:
        return s
    raise ValueError("unknown architecture")


def _convert_cpu(s):
    value = float(s)
    if value >= 0:
        return value
    raise ValueError("must be non-negative")


def _convert_mem(s):
    if s[-1] in _MEM_SUFFIXES:
        value = float(s[:-1]) * _MEM_SUFFIXES[s[-1]]
    else:
        value = float(s)
    if value >= 0:
        return value
    raise ValueError("must be non-negative")