| Trees | Indices | Help |
|---|
|
|
1 #
2 # Decoding data from XML streams
3 #
4 # Copyright (c) 2008--2018 Red Hat, Inc.
5 #
6 # This software is licensed to you under the GNU General Public License,
7 # version 2 (GPLv2). There is NO WARRANTY for this software, express or
8 # implied, including the implied warranties of MERCHANTABILITY or FITNESS
9 # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
10 # along with this software; if not, see
11 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12 #
13 # Red Hat trademarks are not licensed under GPLv2. No permission is
14 # granted to use or replicate Red Hat trademarks that are incorporated
15 # in this software or its documentation.
16 #
17
18 import sys
19 import re
20 from xml.sax import make_parser, SAXParseException, ContentHandler, \
21 ErrorHandler
22
23 from spacewalk.common import usix
24 from spacewalk.common import rhnFlags
25 from spacewalk.common.rhnLog import log_debug
26 from spacewalk.common.rhnConfig import CFG
27 from spacewalk.common.rhnTB import Traceback
28 from spacewalk.server.importlib import importLib, backendLib
29
30 RHEL234_REGEX = re.compile("rhel-[^-]*-[aew]s-(4|3|2.1)")
31
32 # Terminology used throughout this file:
33 # Item: an atomic entity from the database's perspective.
34 # A channel, or a package, or an erratum is an item.
35 # Container: a list of items
36 # We work under the assumption everything on the second level (i.e. a child
37 # of the root element) is a container.
38
39 # The way the parser works: get a handler with getHandler() and call process()
40 # with an XML stream.
41
42 # Our parser exceptions
43
44
45 -class ParseException(Exception):
50
57
60
62 ParseException.__init__(self, *args)
63 self.stream_version = stream_version
64 self.parser_version = parser_version
65
66 # XML parser exception wrappers
67 # Exposed functionality for the next three include:
68 # getColumnNumber(), getLineNumber(), and _msg (or just str(e))
69
70
71 -class RecoverableParseException(SAXParseException, Exception):
72
73 """exception wrapper for a critical, but possibly recoverable, XML parser
74 error.
75 """
76 pass
77
84
89
91 self.name = name
92 if attributes is None:
93 attributes = {}
94 if subelements is None:
95 subelements = []
96 self.attributes = attributes
97 self.subelements = subelements
98
101
103 return "[<Node element: name=%s>]" % self.name
104
105
106 # Base class we use as a SAX parsing handler
107 -class BaseDispatchHandler(ContentHandler, ErrorHandler):
108
109 """ Base class we use as a SAX parsing handler
110
111 We expect the meaningful data to be on the third level.
112 The root element defines what the export contains, while the collection
113 element defines what this collection contains
114 """
115 rootElement = None # non-static
116 __stream = None
117 container_dispatch = {}
118
120 ContentHandler.__init__(self)
121 self.rootAttributes = None
122 self.__parser = make_parser()
123 # Init the parser's handlers
124 self.restoreParser()
125 # No container at this time
126 self.__container = None
127 # Reset all the containers, to make sure previous runs don't leave
128 # garbage data
129 for container in self.container_dispatch.values():
130 container.reset()
131
133 # Restore the parser's handlers to self
134 self.__parser.setContentHandler(self)
135 self.__parser.setErrorHandler(self)
136
137 @staticmethod
140
141 # Starts processing the data from the XML stream
143 log_debug(6)
144 if stream is not None:
145 self.setStream(stream)
146 try:
147 self.__parser.parse(self.__stream)
148 except (KeyboardInterrupt, SystemExit):
149 raise
150 except Exception: # pylint: disable=E0012, W0703
151 Traceback(ostream=sys.stderr, with_locals=1)
152 if stream is not None:
153 stream.close()
154 sys.exit(1)
155
160
162 # WARNING: better call this function when you're done, or you'll end
163 # up with a circular reference
164 self.__parser = None
165
167 # clear out the current container's parse batch; start afresh
168 if self.__container:
169 try:
170 self.__container.batch = []
171 except (KeyboardInterrupt, SystemExit):
172 raise
173 except Exception:
174 e = sys.exc_info()[1]
175 log_debug(-1, 'ERROR (odd) upon container.batch=[] cleanup: %s' % e)
176 raise
177
178 # Interface with containers
180 if not hasattr(obj, "container_name"):
181 raise Exception("%s not a container type" % type(obj))
182
183 # reset the container (to clean up garbage from previous parses)
184 obj.reset()
185 self.container_dispatch[obj.container_name] = obj
186
188 if name not in self.container_dispatch:
189 # Return a dummy container
190 c = ContainerHandler()
191 c.container_name = name
192 return c
193
194 return self.container_dispatch[name]
195
198
199 # Overwrite the functions required by SAX
201 ContentHandler.setDocumentLocator(self, locator)
202
203 # def startDocument(self):
204
205 # def endDocument(self):
206
208 log_debug(6, name)
209 utf8_attrs = _dict_to_utf8(attrs)
210 if self.rootAttributes is None:
211 # First time around
212 if self.rootElement != name:
213 raise Exception("Mismatching elements; root='%s', "
214 "received='%s'" % (self.rootElement, name))
215 self.rootAttributes = utf8_attrs
216 self._check_version()
217 return
218
219 if self.__container is None:
220 # This means it's parsing a container element
221 self.__container = self.get_container(name)
222
223 self.__container.startElement(name, utf8_attrs)
224
228
230 log_debug(6, name)
231 if self.__container is None:
232 # End of the root attribute
233 # We know now the tag stack is empty
234 self.rootAttributes = None
235 return
236
237 try:
238 self.__container.endElement(name)
239 except _EndContainerEvent:
240 self.__container = None
241
242 #___Error handling methods___
243
244 # pylint: disable=W0212,W0710
246 """Handle a recoverable error.
247 """
248 log_debug(-1, "ERROR (RECOVERABLE): parse error encountered - line: %s, col: %s, msg: %s"
249 % (exception.getLineNumber(), exception.getColumnNumber(), exception._msg))
250 raise RecoverableParseException(exception._msg, exception, exception._locator)
251
253 """Handle a non-recoverable error.
254 """
255 log_debug(-1, "ERROR (FATAL): parse error encountered - line: %s, col: %s, msg: %s"
256 % (exception.getLineNumber(), exception.getColumnNumber(), exception._msg))
257 raise FatalParseException(exception._msg, exception, exception._locator)
258
260 """Handle a warning.
261 """
262 log_debug(-1, "ERROR (WARNING): parse error encountered - line: %s, col: %s, msg: %s"
263 % (exception.getLineNumber(), exception.getColumnNumber(), exception._msg))
264
265 # To be overridden in subclasses
268
269 # Particular case: a satellite handler
270
271
272 -class SatelliteDispatchHandler(BaseDispatchHandler):
273 rootElement = 'rhn-satellite'
274 # this is the oldest version of channel dump we support
275 version = "3.0"
276
277 # Historical log
278 # * Version 2.2 2004-03-02
279 # arch types introduced in all the arch dumps
280 # * Version 2.3 2004-09-13
281 # added short package dumps per channel
282 # * Version 3.0 2005-01-13
283 # required major version change for channel family merging (#136525)
284
286 # Check the version
287 version = self.rootAttributes.get("version")
288 # Entitlement/certificate generation
289 generation = self.rootAttributes.get("generation")
290 rhnFlags.set("stream-generation", generation)
291 if not version:
292 version = "0"
293 stream_version = list(map(int, version.split('.')))
294 allowed_version = list(map(int, self.version.split(".")))
295 if (stream_version[0] != allowed_version[0] or
296 stream_version[1] < allowed_version[1]):
297 raise IncompatibleVersionError(version, self.version,
298 "Incompatible stream version %s; code supports %s" % (
299 version, self.version))
300
305 item_name = None
306 item_class = object
307 tagMap = {}
308
311
313 item = self.item_class()
314 # Populate the item from the attribute data structure
315 self.populateFromAttributes(item, attributes)
316 # Populate the item from sub-elements
317 self.populateFromElements(item, elements)
318 return item
319
321 # Populates dict with items from sourceDict
322 for key, value in sourceDict.items():
323 if key not in self.tagMap:
324 if key not in obj:
325 # Unsupported key
326 continue
327 else:
328 # Have to map this key
329 key = self.tagMap[key]
330
331 # Finally, update the key
332 obj[key] = _normalizeAttribute(obj.attributeTypes.get(key), value)
333
335 # Populates obj with `elements' as subelements
336 keys = list(obj.keys())
337 keys_len = len(keys)
338 for element in elements:
339 if _is_string(element):
340 if keys_len != 1:
341 if not element.strip():
342 # White space around an element - skip
343 continue
344 # Ambiguity: don't know which attribute to initialize
345 raise Exception("Ambiguity %s" % keys)
346 # Init the only attribute we know of
347 obj[keys[0]] = element
348 continue
349 name = element.name
350 if name not in obj and name not in self.tagMap:
351 # Unsupported key
352 continue
353 if name in self.tagMap:
354 # Have to map this element
355 name = self.tagMap[name]
356 value = _normalizeSubelements(obj.attributeTypes.get(name),
357 element.subelements)
358 obj[name] = value
359
362 if isinstance(obj, usix.StringType):
363 return 1
364 if isinstance(obj, usix.UnicodeType):
365 return 1
366 return 0
367
370 # Accelerate the most common cases
371 if isinstance(data, usix.StringType):
372 return data
373 elif isinstance(data, usix.UnicodeType):
374 return data.encode('UTF8')
375 return str(data)
376
379 # Convert the dictionary to have non-unocide key-value pairs
380 ret = {}
381 for k, v in d.items():
382 if isinstance(k, usix.UnicodeType):
383 k = k.encode('UTF8')
384 if isinstance(v, usix.UnicodeType):
385 v = v.encode('UTF8')
386 ret[k] = v
387 return ret
388
389
390 __itemDispatcher = {}
395
398 # Creates an Item object from the specified element
399 if element.name not in __itemDispatcher:
400 # No item processor
401 return None
402 item = __itemDispatcher[element.name]()
403 return item.populate(element.attributes, element.subelements)
404
412
417 addItem(ServerArchItem)
423 addItem(PackageArchItem)
429 addItem(ChannelArchItem)
435 addItem(CPUArchItem)
441 addItem(ServerPackageArchCompatItem)
447 addItem(ServerChannelArchCompatItem)
451 item_name = 'rhn-channel-package-arch-compat'
452 item_class = importLib.ChannelPackageArchCompat
453 addItem(ChannelPackageArchCompatItem)
457 item_name = 'rhn-server-group-server-arch-compat'
458 item_class = importLib.ServerGroupServerArchCompat
459 addItem(ServerGroupServerArchCompatItem)
463 item_name = 'rhn-channel-family'
464 item_class = importLib.ChannelFamily
465 tagMap = {
466 'id': 'channel-family-id',
467 # max_members is no longer populated from the xml dump, but from the
468 # satellite cert
469 'rhn-channel-family-name': 'name',
470 'rhn-channel-family-product-url': 'product_url',
471 'channel-labels': 'channels',
472 }
473 addItem(ChannelFamilyItem)
477 item_name = 'rhn-channel'
478 item_class = importLib.Channel
479 tagMap = {
480 'channel-id': 'string_channel_id',
481 'org-id': 'org_id',
482 'rhn-channel-parent-channel': 'parent_channel',
483 'rhn-channel-families': 'families',
484 'channel-arch': 'channel_arch',
485 'rhn-channel-basedir': 'basedir',
486 'rhn-channel-name': 'name',
487 'rhn-channel-summary': 'summary',
488 'rhn-channel-description': 'description',
489 'rhn-channel-last-modified': 'last_modified',
490 'rhn-dists': 'dists',
491 'rhn-release': 'release',
492 'channel-errata': 'errata',
493 'kickstartable-trees': 'kickstartable_trees',
494 'rhn-channel-errata': 'errata_timestamps',
495 'source-packages': 'source_packages',
496 'rhn-channel-gpg-key-url': 'gpg_key_url',
497 'rhn-channel-product-name': 'product_name',
498 'rhn-channel-product-version': 'product_version',
499 'rhn-channel-product-beta': 'product_beta',
500 'rhn-channel-receiving-updates': 'receiving_updates',
501 'rhn-channel-checksum-type': 'checksum_type',
502 'rhn-channel-comps-last-modified': 'comps_last_modified',
503 'rhn-channel-modules-last-modified': 'modules_last_modified',
504 'sharing': 'channel_access',
505 'rhn-channel-trusted-orgs': 'trust_list',
506 }
507
509 # bz 808516, to retain compatibility with Satellite <= 5.3 we
510 # need to assume sha1 checksum type unless we explicitly see
511 # 'rhn-null' in the xml
512 checksum_type_really_null = False
513 for element in elements:
514 if (not _is_string(element)
515 and element.name == 'rhn-channel-checksum-type'):
516 for subelement in element.subelements:
517 if (not _is_string(subelement)
518 and subelement.name == 'rhn-null'):
519 checksum_type_really_null = True
520
521 BaseItem.populateFromElements(self, obj, elements)
522
523 if obj['checksum_type'] == 'sha':
524 obj['checksum_type'] = 'sha1'
525 if not obj['checksum_type'] and not checksum_type_really_null:
526 obj['checksum_type'] = 'sha1'
527
528 # if importing from an old export that does not know about
529 # channel_access, use the default
530 if not obj['channel_access']:
531 obj['channel_access'] = 'private'
532
533 # if using versions of rhel that doesn't use yum, set
534 # checksum_type to None
535 if (RHEL234_REGEX.match(obj['label'])
536 or (obj['parent_channel']
537 and RHEL234_REGEX.match(obj['parent_channel']))):
538 obj['checksum_type'] = None
539
540 addItem(ChannelItem)
544 item_name = 'rhn-channel-trusted-org'
545 item_class = importLib.ChannelTrust
546 tagMap = {
547 'org-id': 'org_trust_id',
548 }
549 addItem(ChannelTrustItem)
553 item_name = 'rhn-org-trust'
554 item_class = importLib.OrgTrust
555 tagMap = {
556 'org-id': 'org_id',
557 }
558 addItem(OrgTrustItem)
562 item_name = 'rhn-org'
563 item_class = importLib.Org
564 tagMap = {
565 'id': 'id',
566 'name': 'name',
567 'rhn-org-trusts': 'org_trust_ids',
568 }
569 addItem(OrgItem)
573
575 item = BaseItem.populate(self, attributes, elements)
576 item['checksums'] = {}
577 if 'md5sum' in item:
578 # xml dumps < 3.6 (aka pre-sha256)
579 item['checksums']['md5'] = item['md5sum']
580 del(item['md5sum'])
581 if 'checksum_list' in item and item['checksum_list']:
582 for csum in item['checksum_list']:
583 item['checksums'][csum['type']] = csum['value']
584 del(item['checksum_list'])
585 for ctype in CFG.CHECKSUM_PRIORITY_LIST:
586 if ctype in item['checksums']:
587 item['checksum_type'] = ctype
588 item['checksum'] = item['checksums'][ctype]
589 break
590 return item
591 addItem(BaseChecksummedItem)
595 item_name = 'rhn-package-short'
596 item_class = importLib.IncompletePackage
597 tagMap = {
598 'id': 'package_id',
599 'package-size': 'package_size',
600 'last-modified': 'last_modified',
601 'package-arch': 'arch',
602 'org-id': 'org_id',
603 'checksums': 'checksum_list',
604 }
605 addItem(IncompletePackageItem)
609 item_name = 'checksum'
610 item_class = importLib.Checksum
611 tagMap = {
612 'checksum-type': 'type',
613 'checksum-value': 'value',
614 }
615 addItem(ChecksumItem)
619 item_name = 'rhn-package'
620 item_class = importLib.Package
621 tagMap = {
622 # Stuff coming through as attributes
623 'package-group': 'package_group',
624 'rpm-version': 'rpm_version',
625 'payload-size': 'payload_size',
626 'build-host': 'build_host',
627 'build-time': 'build_time',
628 'source-rpm': 'source_rpm',
629 'payload-format': 'payload_format',
630 # Stuff coming through as subelements
631 'rhn-package-summary': 'summary',
632 'rhn-package-description': 'description',
633 'rhn-package-vendor': 'vendor',
634 'rhn-package-copyright': 'license',
635 'rhn-package-header-sig': 'header_sig',
636 # These are duplicated as attributes, should go away eventually
637 'rhn-package-package-group': 'package_group',
638 'rhn-package-rpm-version': 'rpm_version',
639 'rhn-package-payload-size': 'payload_size',
640 'rhn-package-header-start': 'header_start',
641 'rhn-package-header-end': 'header_end',
642 'rhn-package-build-host': 'build_host',
643 'rhn-package-build-time': 'build_time',
644 'rhn-package-source-rpm': 'source_rpm',
645 'rhn-package-payload-format': 'payload_format',
646 'rhn-package-cookie': 'cookie',
647 #
648 'rhn-package-files': 'files',
649 'rhn-package-requires': 'requires',
650 'rhn-package-provides': 'provides',
651 'rhn-package-conflicts': 'conflicts',
652 'rhn-package-obsoletes': 'obsoletes',
653 'rhn-package-recommends': 'recommends',
654 'rhn-package-suggests': 'suggests',
655 'rhn-package-supplements': 'supplements',
656 'rhn-package-enhances': 'enhances',
657 'rhn-package-changelog': 'changelog',
658 }
659 tagMap.update(IncompletePackageItem.tagMap)
660
662 item = IncompletePackageItem.populate(self, attributes, elements)
663 # find out "primary" checksum
664 # pylint: disable=bad-option-value,unsubscriptable-object,unsupported-assignment-operation
665 have_filedigests = len([1 for i in item['requires'] if i['name'] == 'rpmlib(FileDigests)'])
666 if not have_filedigests:
667 item['checksum_type'] = 'md5'
668 item['checksum'] = item['checksums']['md5']
669 return item
670 addItem(PackageItem)
674 item_name = 'source-package'
675 item_class = importLib.IncompleteSourcePackage
676 tagMap = {
677 'last-modified': 'last_modified',
678 'source-rpm': 'source_rpm',
679 }
680 addItem(IncompleteSourcePackageItem)
684 item_name = 'rhn-source-package'
685 item_class = importLib.SourcePackage
686 tagMap = {
687 'id': 'package_id',
688 'source-rpm': 'source_rpm',
689 'package-group': 'package_group',
690 'rpm-version': 'rpm_version',
691 'payload-size': 'payload_size',
692 'build-host': 'build_host',
693 'build-time': 'build_time',
694 'package-size': 'package_size',
695 'last-modified': 'last_modified',
696 }
697 addItem(SourcePackageItem)
701 item_name = 'rhn-package-changelog-entry'
702 item_class = importLib.ChangeLog
703 tagMap = {
704 'rhn-package-changelog-entry-name': 'name',
705 'rhn-package-changelog-entry-text': 'text',
706 'rhn-package-changelog-entry-time': 'time',
707 }
708 addItem(ChangelogItem)
712
713 """virtual class - common settings for dependency items"""
714 item_class = importLib.Dependency
715 tagMap = {
716 'sense': 'flags',
717 }
718
721 item_name = 'rhn-package-provides-entry'
722 addItem(ProvidesItem)
726 item_name = 'rhn-package-requires-entry'
727 addItem(RequiresItem)
731 item_name = 'rhn-package-conflicts-entry'
732 addItem(ConflictsItem)
736 item_name = 'rhn-package-obsoletes-entry'
737 addItem(ObsoletesItem)
741 item_name = 'rhn-package-recommends-entry'
742 addItem(RecommendsItem)
746 item_name = 'rhn-package-suggests-entry'
747 addItem(SuggestsItem)
751 item_name = 'rhn-package-supplements-entry'
752 addItem(SupplementsItem)
756 item_name = 'rhn-package-enhances-entry'
757 addItem(EnhancesItem)
761 item_name = 'rhn-package-file'
762 item_class = importLib.File
763 tagMap = {
764 'checksum-type': 'checksum_type',
765 }
766
768 if 'md5' in attributes and 'checksum-type' not in attributes:
769 attributes['checksum-type'] = 'md5'
770 attributes['checksum'] = attributes['md5']
771 item = BaseChecksummedItem.populate(self, attributes, elements)
772 return item
773 addItem(FileItem)
777 item_name = 'rhn-dist'
778 item_class = importLib.DistChannelMap
779 tagMap = {
780 'channel-arch': 'channel_arch',
781 }
782 addItem(DistItem)
786 item_name = 'erratum'
787 item_class = importLib.ChannelErratum
788 tagMap = {
789 'last-modified': 'last_modified',
790 'advisory-name': 'advisory_name',
791 }
792 addItem(ChannelErratumItem)
796 item_name = 'rhn-release'
797 item_class = importLib.ReleaseChannelMap
798 tagMap = {
799 'channel-arch': 'channel_arch'
800 }
801 addItem(ReleaseItem)
805 item_name = 'rhn-erratum-bug'
806 item_class = importLib.Bug
807 tagMap = {
808 'rhn-erratum-bug-id': 'bug_id',
809 'rhn-erratum-bug-summary': 'summary',
810 'rhn-erratum-bug-href': 'href',
811 }
812 addItem(BugItem)
820 addItem(KeywordItem)
824 item_name = 'rhn-erratum'
825 item_class = importLib.Erratum
826 tagMap = {
827 'id': 'erratum_id',
828 'org-id': 'org_id',
829 'rhn-erratum-advisory-name': 'advisory_name',
830 'rhn-erratum-advisory-rel': 'advisory_rel',
831 'rhn-erratum-advisory-type': 'advisory_type',
832 'rhn-erratum-product': 'product',
833 'rhn-erratum-description': 'description',
834 'rhn-erratum-synopsis': 'synopsis',
835 'rhn-erratum-topic': 'topic',
836 'rhn-erratum-solution': 'solution',
837 'rhn-erratum-issue-date': 'issue_date',
838 'rhn-erratum-update-date': 'update_date',
839 'rhn-erratum-notes': 'notes',
840 'rhn-erratum-org-id': 'org_id',
841 'rhn-erratum-refers-to': 'refers_to',
842 'rhn-erratum-channels': 'channels',
843 'rhn-erratum-keywords': 'keywords',
844 'rhn-erratum-checksums': 'checksums',
845 'rhn-erratum-bugs': 'bugs',
846 'rhn-erratum-cve': 'cve',
847 'rhn-erratum-last-modified': 'last_modified',
848 'rhn-erratum-files': 'files',
849 'rhn-erratum-errata-from': 'errata_from',
850 'rhn-erratum-severity': 'severity_id',
851 'cve-names': 'cve',
852 }
853 addItem(ErratumItem)
859 addItem(ErrorItem)
863 item_name = 'rhn-erratum-file'
864 item_class = importLib.ErrataFile
865 tagMap = {
866 'type': 'file_type',
867 'channels': 'channel_list',
868 # Specific to XML
869 'package': 'package',
870 'source-package': 'source-package',
871 'checksum-type': 'checksum_type',
872 }
873 addItem(ErrataFileItem)
879 addItem(ProductNamesItem)
883 item_name = 'rhn-kickstartable-tree'
884 item_class = importLib.KickstartableTree
885 tagMap = {
886 'rhn-kickstart-files': 'files',
887 'base-path': 'base_path',
888 'boot-image': 'boot_image',
889 'kstree-type-label': 'kstree_type_label',
890 'install-type-label': 'install_type_label',
891 'kstree-type-name': 'kstree_type_name',
892 'install-type-name': 'install_type_name',
893 'last-modified': 'last_modified',
894 }
895 addItem(KickstartableTreeItem)
899 item_name = 'rhn-kickstart-file'
900 item_class = importLib.KickstartFile
901 tagMap = {
902 'relative-path': 'relative_path',
903 'file-size': 'file_size',
904 'last-modified': 'last_modified',
905 'checksums': 'checksum_list',
906 }
907 addItem(KickstartFileItem)
915 container_name = None
916
918 # The tag stack; each item is an array [element, attributes]
919 self.tagStack = []
920 # The object stack; each item is an array
921 # [element, attributes, content]
922 self.objStack = []
923 # Collects the elements in a batch
924 self.batch = []
925
927 # Make sure the batch is preserved
928 batch = self.batch
929 # Re-init the object: cleans up the stacks and such
930 self.__init__()
931 # And restore the batch
932 self.batch = batch
933
935 # log_debug(6, element) --duplicate logging.
936 if not self.tagStack and element != self.container_name:
937 # Strange; this element is called to parse stuff when it's not
938 # supposed to
939 raise Exception('This object should not have been used')
940 self.tagStack.append(Node(element, attrs))
941 self.objStack.append([])
942
944 log_debug(6, data)
945 if data == '':
946 # Nothing to do
947 return
948 # If the thing in front is a string, append to it
949 lastObj = self.objStack[-1]
950 if lastObj and _is_string(lastObj[-1]):
951 lastObj[-1] = '%s%s' % (lastObj[-1], data)
952 else:
953 lastObj.append(data)
954
956 # log_debug(6, element) --duplicate logging.
957 tagobj = self.tagStack[-1]
958 # Remove the previous tag
959 del self.tagStack[-1]
960 # Decode the tag object
961 name = tagobj.name
962 if name != element:
963 raise ParseException(
964 "incorrect XML data: closing tag %s, opening tag %s" % (
965 element, name))
966 # Append the content of the object to the tag object
967 for obj in self.objStack[-1]:
968 tagobj.addSubelement(obj)
969
970 # Remove the subelements from the stack
971 del self.objStack[-1]
972
973 if not self.objStack:
974 # End element for this container
975 self.endContainerCallback()
976 raise _EndContainerEvent(tagobj)
977
978 # Regular element; append the current object as a subelement to the
979 # previous object
980 self.objStack[-1].append(tagobj)
981 if len(self.tagStack) == 1:
982 # Finished parsing an item; let the parent know
983 self.endItemCallback()
984
987
990
992 # Grab the latest object we've parsed
993 obj = self.getLastItem()
994 # And remove it since we don't need it
995 self.clearLastItem()
996 # Instantiate the object
997 item = _createItem(obj)
998
999 if item is None:
1000 # Nothing to do with this object
1001 return
1002
1003 if 'error' in item:
1004 # Special case errors
1005 log_debug(0, 'XML parser error: found "rhn-error" item: %s' %
1006 item['error'])
1007 raise ParseException(item['error'])
1008
1009 self.postprocessItem(item)
1010 # Add it to the items list
1011 self.batch.append(item)
1012
1015
1019
1022 # pylint: disable=R0911
1023 # Deal with simple cases first
1024 if objtype is None:
1025 # Don't know how to handle it
1026 return _stringify(subelements)
1027
1028 if not subelements:
1029 # No subelements available
1030 if isinstance(objtype, usix.ListType):
1031 # Expect a list of things - return the empty list
1032 return []
1033 # Expected a scalar type
1034 return None
1035
1036 # We do have subelements
1037 # Extract all the non-string subelements
1038 _s = []
1039 _strings_only = 1
1040 for subel in subelements:
1041 if _is_string(subel) and not subel.strip():
1042 # Ignore it for now
1043 continue
1044 _s.append(subel)
1045 if not _is_string(subel):
1046 _strings_only = 0
1047
1048 if _strings_only:
1049 # Multiple strings - contactenate into one
1050 subelements = [''.join(subelements)]
1051 else:
1052 # Ignore whitespaces around elements
1053 subelements = _s
1054
1055 if not isinstance(objtype, usix.ListType):
1056 if len(subelements) > 1:
1057 raise Exception("Expected a scalar, got back a list")
1058 subelement = subelements[0]
1059 # NULL?
1060 if isinstance(subelement, Node):
1061 if subelement.name == 'rhn-null':
1062 return None
1063 raise Exception("Expected a scalar, got back an element '%s'" % subelement.name)
1064
1065 if objtype is usix.StringType:
1066 return _stringify(subelement)
1067
1068 if objtype is usix.IntType:
1069 if subelement == '':
1070 # Treat it as NULL
1071 return None
1072 return int(subelement)
1073
1074 if objtype is importLib.DateType:
1075 return _normalizeDateType(subelement)
1076 raise Exception("Unhandled type %s for subelement %s" % (objtype,
1077 subelement))
1078
1079 # Expecting a list of things
1080 expectedType = objtype[0]
1081 if expectedType is usix.StringType:
1082 # List of strings
1083 return list(map(_stringify, subelements))
1084
1085 if expectedType is usix.IntType:
1086 # list of ints
1087 return list(map(int, subelements))
1088
1089 if expectedType is importLib.DateType:
1090 return list(map(_normalizeDateType, subelements))
1091
1092 # A subelement
1093 result = []
1094 for subelement in subelements:
1095 item = _createItem(subelement)
1096 if item is None:
1097 # Item processor not found
1098 continue
1099 if not isinstance(item, expectedType):
1100 raise Exception("Expected type %s, got back %s %s" % (expectedType,
1101 type(item), item))
1102 result.append(item)
1103
1104 return result
1105
1108 # Deal with simple cases first
1109 if (objtype is None) or (objtype is usix.StringType):
1110 # (Don't know how to handle it) or (Expecting a scalar)
1111 return attribute
1112 elif objtype is usix.IntType:
1113 if attribute == '' or attribute == 'None':
1114 # Treat it as NULL
1115 return None
1116 else:
1117 return int(attribute)
1118 elif objtype is importLib.DateType:
1119 return _normalizeDateType(attribute)
1120 elif isinstance(objtype, usix.ListType):
1121 # List type - split stuff
1122 return attribute.split()
1123 else:
1124 raise Exception("Unhandled attribute data type %s" % objtype)
1125
1128 try:
1129 value = int(value)
1130 except ValueError:
1131 # string
1132 return value
1133 # Timestamp
1134 return backendLib.localtime(value)
1135
1136
1137 #
1138 # Containers:
1139 #
1140 # XXX: we'll need an ErrorContainer eventually
1141 # (we do not handle <rhn-error> properly if it is
1142 # a "root" element).
1143 # class ErrorContainer(ContainerHandler):
1144 # container_name = 'rhn-error'
1145 # def endContainerCallback(self):
1146 # lastObj = self.getLastItem()
1147 # raise ParseException(lastObj)
1148
1149
1150 -class ChannelFamilyContainer(ContainerHandler):
1151 container_name = 'rhn-channel-families'
1152
1155 container_name = 'rhn-channels'
1156
1168
1171
1172 """Inherits from IncompletePackageContainer, since we need to postprocess the
1173 channel information
1174 """
1175 container_name = 'rhn-packages'
1176
1179 container_name = 'rhn-source-packages'
1180
1183 container_name = 'rhn-errata'
1184
1187 container_name = 'rhn-server-arches'
1188
1191 container_name = 'rhn-package-arches'
1192
1195 container_name = 'rhn-channel-arches'
1196
1199 container_name = 'rhn-cpu-arches'
1200
1203 container_name = 'rhn-server-package-arch-compatibility-map'
1204
1207 container_name = 'rhn-server-channel-arch-compatibility-map'
1208
1211 container_name = 'rhn-channel-package-arch-compatibility-map'
1212
1215 container_name = 'rhn-server-group-server-arch-compatibility-map'
1216
1219 container_name = 'rhn-product-names'
1220
1223 container_name = 'rhn-kickstartable-trees'
1224
1227 container_name = 'rhn-orgs'
1228
| Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Wed Mar 4 07:37:45 2020 | http://epydoc.sourceforge.net |