This file is indexed.

/usr/lib/python3/dist-packages/pypowervm/entities.py is in python3-pypowervm 1.1.10-0ubuntu1.

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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
# Copyright 2014, 2015 IBM Corp.
#
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""High-level pythonic abstractions of XML entities returned by PowerVM."""

import collections
import copy
import re

from lxml import etree

from pypowervm import const
from pypowervm import util


class Atom(object):
    def __init__(self, properties):
        self.properties = properties

    @property
    def uuid(self):
        try:
            return self.properties['id']
        except KeyError:
            return None

    @property
    def links(self):
        """Get the rel-keyed dict of <link/>s for this Atom, or {} if none."""
        return self.properties.get('links', {})

    @property
    def self_link(self):
        """Get the *first* SELF link, or None if none exists."""
        return self.links.get('SELF', [None])[0]

    @classmethod
    def _process_props(cls, el, props):
        pat = '{%s}' % const.ATOM_NS
        if re.match(pat, el.tag):
            # strip off atom namespace qualification for easier access
            param_name = el.tag[el.tag.index('}') + 1:]
        else:
            # leave qualified anything that is not in the atom
            # namespace
            param_name = el.tag
        if param_name == 'link':
            # properties['links'][REL] = [href, ...]
            # Note that rel may (legally) be None
            rel = el.get('rel')
            if rel:
                rel = rel.upper()
            href = el.get('href')
            if 'links' not in props:
                props['links'] = collections.defaultdict(list)
            props['links'][rel].append(href)
        elif param_name == 'category':
            props[param_name] = el.get('term')
        elif param_name == '{%s}etag' % const.UOM_NS:
            props['etag'] = el.text
        elif el.text:
            props[param_name] = el.text


class Feed(Atom):
    """Represents an Atom Feed returned from PowerVM."""
    def __init__(self, properties, entries):
        super(Feed, self).__init__(properties)
        self.entries = entries

    def findentries(self, subelem, text):
        entries = []
        for entry in self.entries:
            subs = entry.element.findall(subelem)
            for s in subs:
                if s.text == text:
                    entries.append(entry)
                    break
        return entries

    @classmethod
    def unmarshal_atom_feed(cls, feedelem, resp):
        """Factory method producing a Feed object from a parsed ElementTree

        :param feedelem: Parsed ElementTree object representing an atom feed.
        :param resp: The Response from which this Feed was parsed.
        :return: a new Feed object representing the feedelem parameter.
        """
        ret = cls({}, [])
        for child in list(feedelem):
            if child.tag == str(etree.QName(const.ATOM_NS, 'entry')):
                # NB: The use of ret.self_link here relies on <entry>s being
                # AFTER the self link in the <feed>.  (They are in fact last.)
                ret.entries.append(Entry.unmarshal_atom_entry(child, resp))
            elif not list(child):
                cls._process_props(child, ret.properties)
        return ret


class Entry(Atom):
    """Represents an Atom Entry returned by the PowerVM API."""
    def __init__(self, properties, element, adapter):
        """Create an Entry from an etree.Element representing a PowerVM object.

        :param properties: Dict of <entry>-level properties as produced by
                           unmarshal_atom_entry.
        :param element: etree.Element (not entities.Element) - the root of the
                        PowerVM object (not the <feed>, <entry>, or <content>).
        :param adapter: pypowervm.adapter.Adapter through which the element was
                        fetched, and/or through which it should be updated.
        """
        super(Entry, self).__init__(properties)
        self.element = Element.wrapelement(element, adapter)

    def __deepcopy__(self, memo=None):
        """Produce a deep (except for adapter) copy of this Entry."""
        return self.__class__(copy.deepcopy(self.properties, memo=memo),
                              copy.deepcopy(self.element, memo=memo).element,
                              self.adapter)

    @property
    def etag(self):
        return self.properties.get('etag', None)

    @property
    def adapter(self):
        return self.element.adapter

    @classmethod
    def unmarshal_atom_entry(cls, entryelem, resp):
        """Factory method producing an Entry object from a parsed ElementTree

        :param entryelem: Parsed ElementTree object representing an atom entry.
        :param resp: The Response containing (the feed containing) the entry.
        :return: a new Entry object representing the entryelem parameter.
        """
        entryprops = {}
        element = None
        for child in list(entryelem):
            if child.tag == str(etree.QName(const.ATOM_NS, 'content')):
                # PowerVM API only has one element per entry
                element = child[0]
            elif not list(child):
                cls._process_props(child, entryprops)
        return cls(entryprops, element, resp.adapter)


class Element(object):
    """Represents an XML element - a utility wrapper around etree.Element."""
    def __init__(self, tag, adapter, ns=const.UOM_NS, attrib=None, text='',
                 children=(), cdata=False):
        # Defaults shouldn't be mutable
        attrib = attrib if attrib else {}
        self.element = None
        if ns:
            self.element = etree.Element(str(etree.QName(ns, tag)),
                                         attrib=attrib)
        else:
            self.element = etree.Element(tag, attrib=attrib)
        if text:
            self.element.text = etree.CDATA(text) if cdata else text
        for c in children:
            # Use a deep copy, else, c.element gets *removed* from its parent
            # hierarchy (see fourth bullet: http://lxml.de/compatibility.html).
            # Doing the deepcopy here means the caller doesn't have to worry
            # about it.
            self.element.append(copy.deepcopy(c.element))
        self.adapter = adapter

    def __len__(self):
        return len(self.element)

    def __getitem__(self, index):
        return Element.wrapelement(self.element[index], self.adapter)

    def __setitem__(self, index, value):
        if not isinstance(value, Element):
            raise ValueError('Value must be of type Element')
        self.element[index] = value.element

    def __delitem__(self, index):
        del self.element[index]

    def __eq__(self, other):
        if other is None:
            return False
        return self._element_equality(self, other)

    def __deepcopy__(self, memo=None):
        """Produce a deep (except for adapter) copy of this Element."""
        return self.wrapelement(etree.fromstring(self.toxmlstring()),
                                self.adapter)

    @staticmethod
    def _element_equality(one, two):
        """Tests element equality.

        There is no common mechanism for defining 'equality' in the element
        tree.  This provides a good enough equality that meets the schema
        definition.

        :param one: The first element.  Is the backing element.
        :param two: The second element.  Is the backing element.
        :returns: True if the children, text, attributes and tag are equal.
        """

        # Make sure that the children length is equal
        one_children = list(one)
        two_children = list(two)
        if len(one_children) != len(two_children):
            return False

        if one.text != two.text:
            return False

        if one.tag != two.tag:
            return False

        # Recursively validate
        for one_child in one_children:
            found = util.find_equivalent(one_child, two_children)
            if found is None:
                return False

            # Found a match, remove it as it is no longer a valid match.
            # Its equivalence was validated by the upper block.
            two_children.remove(found)

        return True

    def __iter__(self):
        """Returns the children as a list of Elements."""
        return iter([Element.wrapelement(i, self.adapter)
                     for i in list(self.element)])

    @classmethod
    def wrapelement(cls, element, adapter):
        if element is None:
            return None
        # create with minimum inputs
        e = cls('element', adapter)
        # assign element over the one __init__ creates
        e.element = element
        return e

    def toxmlstring(self, pretty=False):
        """Produce an XML dump of this Element.

        :param pretty: If True, format the XML in a visually-pleasing manner.
        :return: An XML string representing this Element.
        """
        # To be sure of backward compatibility, don't pass pretty_print=False.
        kwargs = {'pretty_print': True} if pretty else {}
        return etree.tostring(self.element, **kwargs)

    @property
    def tag(self):
        return etree.QName(self.element.tag).localname

    @tag.setter
    def tag(self, tag):
        ns = self.namespace
        if ns:
            self.element.tag = etree.QName(ns, tag).text
        else:
            self.element.tag = tag

    @property
    def namespace(self):
        ns = etree.QName(self.element.tag).namespace
        return '' if ns is None else ns

    @namespace.setter
    def namespace(self, ns):
        self.element.tag = etree.QName(ns, self.tag).text

    @property
    def text(self):
        return self.element.text

    @text.setter
    def text(self, text):
        self.element.text = text

    @property
    def attrib(self):
        return self.element.attrib

    @attrib.setter
    def attrib(self, attrib):
        self.element.attrib = attrib

    def get(self, key, default=None):
        """Gets the element attribute named key.

        Returns the attribute value, or default if the attribute was not found.
        """
        return self.element.get(key, default)

    def items(self):
        """Returns the element attributes as a sequence of (name, value) pairs.

        The attributes are returned in an arbitrary order.
        """
        return self.element.items()

    def keys(self):
        """Returns the element attribute names as a list.

        The names are returned in an arbitrary order.
        """
        return self.element.keys()

    def set(self, key, value):
        """Set the attribute key on the element to value."""
        self.element.set(key, value)

    def append(self, subelement):
        """Adds subelement to the end of this element's list of subelements.

        Note: if subelement is a reference to an element within another XML
        hierarchy, it will be *removed* from that hierarchy.  If you intend to
        reuse the parent object, you should pass a copy.deepcopy of the
        subelement to this method.
        """
        # TODO(IBM): We *should* deepcopy to prevent child poaching (see fourth
        # bullet here: http://lxml.de/compatibility.html) - but this breaks the
        # world.  Figure out why, and fix it.
        # self.element.append(copy.deepcopy(subelement.element))
        self.element.append(subelement.element)

    def inject(self, subelement, ordering_list=(), replace=True):
        """Inserts subelement at the correct position in self's children.

        Uses ordering_list to determine the proper spot at which to insert the
        specified subelement.

        :param subelement: The element to inject as a child of this element.
        :param ordering_list: Iterable of string tag names representing the
                              desired ordering of children for this element.
                              If subelement's tag is not included in this list,
                              the behavior is self.append(subelement).
        :param replace: If True, and an existing child with subelement's tag is
                        found, it is replaced.  If False, subelement is added
                        after the existing child(ren).  Note: You probably want
                        to use True only/always when subelement is maxOccurs=1.
                        Conversely, you probably want to use False only/always
                        when subelement is unbounded.  If you use True and more
                        than one matching child is found, the last one is
                        replaced.
        """
        def lname(tag):
            """Localname of a tag (without namespace)."""
            return etree.QName(tag).localname

        children = list(self.element)
        # If no children, just append
        if not children:
            self.append(subelement)
            return

        # Any children with the subelement's tag?
        subfound = self.findall(subelement.tag)
        if subfound:
            if replace:
                self.replace(subfound[-1], subelement)
            else:
                subfound[-1].element.addnext(subelement.element)
            return

        # Now try to figure out insertion point based on ordering_list.
        # Ignore namespaces.
        ordlist = [lname(field) for field in ordering_list]
        subtag = lname(subelement.element.tag)
        # If subelement's tag is not in the ordering list, append
        if subtag not in ordlist:
            self.append(subelement)
            return

        # Get the tags preceding that of subelement
        pres = ordlist[:ordlist.index(subtag)]
        # Find the first child whose tag is not in that list
        for child in children:
            if lname(child.tag) not in pres:
                # Found the insertion point
                child.addprevious(subelement.element)
                return
        # If we got here, all existing children need to precede subelement.
        self.append(subelement)

    def find(self, match):
        """Finds the first subelement matching match.

        :param match: May be a tag name or path.
        :return: an element instance or None.
        """
        qpath = Element._qualifypath(match, self.namespace)
        e = self.element.find(qpath)
        if e is not None:
            # must specify "is not None" here to work
            return Element.wrapelement(e, self.adapter)
        else:
            return None

    def findall(self, match):
        """Finds all matching subelements.

        :param match: May be a tag name or path.
        :return: a list containing all matching elements in document order.
        """
        qpath = Element._qualifypath(match, self.namespace)
        e_iter = self.element.findall(qpath)
        elems = []
        for e in e_iter:
            elems.append(Element.wrapelement(e, self.adapter))
        return elems

    def findtext(self, match, default=None):
        """Finds text for the first subelement matching match.

        :param match: May be a tag name or path.
        :return: the text content of the first matching element, or default
                 if no element was found. Note that if the matching element
                 has no text content an empty string is returned.
        """
        qpath = Element._qualifypath(match, self.namespace)
        text = self.element.findtext(qpath, default)
        return text if text else default

    def insert(self, index, subelement):
        """Inserts subelement at the given position in this element.

        :raises TypeError: if subelement is not an etree.Element.
        """
        self.element.insert(index, subelement.element)

    def iter(self, tag=None):
        """Creates a tree iterator with the current element as the root.

        The iterator iterates over this element and all elements below it, in
        document (depth first) order. If tag is not None or '*', only elements
        whose tag equals tag are returned from the iterator. If the tree
        structure is modified during iteration, the result is undefined.
        """
        # Determine which iterator to use
        # etree.Element.getiterator has been deprecated in favor of
        # etree.Element.iter, but the latter was not added until python 2.7
        if hasattr(self.element, 'iter'):
            lib_iter = self.element.iter
        else:
            lib_iter = self.element.getiterator

        # Fix up the tag value
        if not tag or tag == '*':
            qtag = None
        else:
            qtag = str(etree.QName(self.namespace, tag))

        it = lib_iter(tag=qtag)

        for e in it:
            yield Element.wrapelement(e, self.adapter)

    def replace(self, existing, new_element):
        """Replaces the existing child Element with the new one."""
        self.element.replace(existing.element,
                             new_element.element)

    def remove(self, subelement):
        """Removes subelement from the element.

        Unlike the find* methods this method compares elements based on the
        instance identity, not on tag value or contents.
        """
        self.element.remove(subelement.element)

    @staticmethod
    def _qualifypath(path, ns):
        if not ns:
            return path
        parts = path.split('/')
        for i in range(len(parts)):
            if parts[i] and not re.match(r'[\.\*\[\{]', parts[i]):
                parts[i] = str(etree.QName(ns, parts[i]))
        return '/'.join(parts)


class ElementList(object):
    """Useful list ops on a list of Element.

    In a schema where a simpleType element has a multiplicity allowing more
    than one instance within the containing element, this class provides a way
    to treat those instances as a list, to a limited extent.

    For example, given XML like:
        <parent>
            ...(stuff that isn't <foo/>)...
            <foo>one</foo>
            <foo>two</foo>
            <foo>three</foo>
            ...(stuff that isn't <foo/>)...
        </parent>

        fooList = ElementList(parent_element, 'foo')
        len(fooList)
            3
        repr(fooList)
            "['one', 'two', 'three']"
        'two' in fooList
            True
        'four' in fooList
            False
        fooList.append('four')
        repr(fooList)
            "['one', 'two', 'three', 'four']"
        print root.toxmlstring()
            <parent>
                ...(stuff that isn't <foo/>)...
                <foo>one</foo>
                <foo>two</foo>
                <foo>three</foo>
                <foo>four</foo>
                ...(stuff that isn't <foo/>)...
            </parent>
    """

    def __init__(self, root_elem, tag, ordering_list=()):
        """Initialize a new ElementList.

        Note: The current implementation is limited to simple string elements.
              When inserting new values, they will have the default namespace
              and attrs.

        :param root_elem: The entities.Element representing the parent node
                          containing the elements of interest.
        :param tag: The XML tag of the elements of interest.
        :param ordering_list: Iterable of tag strings indicating the desired
                              overall ordering of the elements within the
                              root_elem.  Used to create the first value in
                              the appropriate spot if the ElementList is
                              initially empty.
        """
        self.root_elem = root_elem
        self.tag = tag
        self.order = ordering_list

    def __find_elems(self):
        """List of entities.Element under self.root_elem with tag self.tag."""
        return self.root_elem.findall(self.tag)

    def __get_values(self):
        """List of the string values within the entities.Element instances."""
        return [elem.text for elem in self.__find_elems()]

    def __create_elem(self, val):
        """Create a new entities.Element suitable for this list.

        :param val: The raw string value for the text content of the new
                    element. E.g. self.__create_elem('foo') will yield an
                    entities.Element representing <tag ...>foo</tag> (where tag
                    is whatever this ElementList was initialized with).
        :return: A new entities.Element containing the specified string val.
        """
        return Element(self.tag, self.root_elem.adapter, text=val)

    def index(self, val):
        return self.__get_values().index(val)

    def __len__(self):
        return len(self.__find_elems())

    def extend(self, val_list):
        for val in val_list:
            self.append(val)

    def __repr__(self):
        return repr(self.__get_values())

    def __contains__(self, val):
        return val in self.__get_values()

    def __str__(self):
        return str(self.__get_values())

    def append(self, val):
        self.root_elem.inject(
            self.__create_elem(val), ordering_list=self.order, replace=False)

    def __getitem__(self, idx):
        return self.__get_values()[idx]

    def __setitem__(self, idx, value):
        self.__find_elems()[idx].text = value

    def __delitem__(self, idx):
        self.root_elem.remove(self.__find_elems()[idx])

    def remove(self, val):
        self.__delitem__(self.__get_values().index(val))

    def __iter__(self):
        return iter(self.__get_values())

    def clear(self):
        for val in self.__get_values():
            self.remove(val)