Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP1:GA
mgr-push.22710
mgr-push-git-0.2168941.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File mgr-push-git-0.2168941.obscpio of Package mgr-push.22710
07070100000000000041FD00000000000000000000000161FA7D4F00000000000000000000000000000000000000000000000900000000mgr-push07070100000001000081B400000000000000000000000161FA7D4F000046AC000000000000000000000000000000000000001100000000mgr-push/COPYING GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. 07070100000002000081B400000000000000000000000161FA7D4F00000026000000000000000000000000000000000000001200000000mgr-push/Makefileinclude ../../../rel-eng/Makefile.git 07070100000003000081B400000000000000000000000161FA7D4F00000367000000000000000000000000000000000000001900000000mgr-push/Makefile.pythonTHIS_MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST))) CURRENT_DIR := $(dir $(THIS_MAKEFILE)) include $(CURRENT_DIR)../../../rel-eng/Makefile.python # Docker tests variables DOCKER_CONTAINER_BASE = devel/galaxy/manager/4.2/docker/containers/suma-4.2 DOCKER_REGISTRY = registry.suse.de DOCKER_RUN_EXPORT = "PYTHONPATH=$PYTHONPATH" DOCKER_VOLUMES = -v "$(CURDIR)/../../../:/manager" __pylint :: $(call update_pip_env) pylint --rcfile=pylintrc $(shell find -name '*.py') > reports/pylint.log || true docker_pylint :: docker run --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/sh -c "cd /manager/client/tools/mgr-push; make -f Makefile.python __pylint" docker_shell :: docker run -t -i --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/bash 07070100000004000081B400000000000000000000000161FA7D4F00000508000000000000000000000000000000000000001A00000000mgr-push/Makefile.rhnpush# Makefile for the rhnpush module # INSTALL = install -D -p --verbose INSTALL_BIN = $(INSTALL) -m 755 INSTALL_DIR = $(INSTALL) -m 755 -d INSTALL_DATA = $(INSTALL) -m 644 SUBDIR = rhnpush FILES = rhnpush_main rhnpush_v2 uploadLib __init__ connection \ rpm2mpm utils rhnpush_cache \ rhnpush_config rhnpush_confmanager archive RHN_CONF_DIR = /etc/sysconfig/rhn RHNPUSHRC = rhnpushrc PYFILES := $(addsuffix .py, $(FILES)) OBJECTS := $(PYFILES) ROOT ?= /usr/share/rhn PYTHON_VERSION ?= 2 # check if we can build man pages DOCBOOK = $(wildcard /usr/bin/docbook2man) SGMLS = $(wildcard *.sgml) MANS = $(patsubst %.sgml,%.8,$(SGMLS)) BINDIR = /usr/bin MANDIR ?= /usr/man EXTRA_DIRS = $(MANDIR)/man8 $(BINDIR) $(RHN_CONF_DIR) install-conf : $(RHNPUSHRC) $(INSTALL_DATA) $(RHNPUSHRC) $(PREFIX)$(RHN_CONF_DIR)/$(RHNPUSHRC) %.inst: rhnpush.inst all : $(MANS) $(OBJECTS) install : all install-conf $(INSTALL_DIR) $(PREFIX)$(MANDIR)/man8 $(INSTALL_DATA) $(MANS) $(PREFIX)$(MANDIR)/man8 $(INSTALL_DIR) $(PREFIX)$(ROOT)/rhnpush $(INSTALL_DATA) $(PYFILES) $(PREFIX)$(ROOT)/rhnpush $(INSTALL_BIN) rhnpush $(PREFIX)$(BINDIR)/rhnpush-$(PYTHON_VERSION) @ln -sfv rhnpush $(PREFIX)$(BINDIR)/rpm2mpm %.8 : %.sgml /usr/bin/docbook2man $< 07070100000005000081B400000000000000000000000161FA7D4F00000265000000000000000000000000000000000000001500000000mgr-push/__init__.py# # Copyright (c) 2008--2015 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # 07070100000006000081B400000000000000000000000161FA7D4F000033C4000000000000000000000000000000000000001400000000mgr-push/archive.py# # Copyright (c) 2008--2017 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # """Archive Parsing module""" import os import subprocess import shutil import tempfile import select import zipfile import tarfile import sys if not hasattr(zipfile, 'ZIP64_LIMIT'): sys.stderr.write("%s requires zipfile with ZIP64 support.\n" % sys.argv[0]) sys.exit(3) # exceptions ------------------------------------------------------------- class ArchiveException(Exception): pass class DecompressionError(ArchiveException): pass class UnknownArchiveError(ArchiveException): pass class InvalidArchiveError(ArchiveException): pass # base archive parsing class --------------------------------------------- class ArchiveParser(object): """Explode an zip or (compressed) tar archive and parse files and directories contained therein""" # constructor -------------------------------------------------------- def __init__(self, archive, tempdir="/tmp/"): """Initialize an archive parser""" assert os.path.exists(archive) self._archive = archive self._archive_dir = None # bug 164756: allow optional working directory self._parent_dir = tempdir # bug: 171086: support for older versions of tempfile (ie python 2.2) tempfile.tempdir = tempdir self._temp_dir = tempfile.mktemp() os.mkdir(self._temp_dir, int('0700', 8)) self._explode() # destructor --------------------------------------------------------- def __del__(self): """Cleanup temporary files and directories""" if hasattr(self, "_temp_dir") and os.path.isdir(self._temp_dir): shutil.rmtree(self._temp_dir, ignore_errors=True) # methods called by constructor -------------------------------------- def _get_archive_dir(self): """[internal] find the archive's top level directory name""" raise NotImplementedError("ArchiveParser: abstract base class method '_get_archive_dir'") def _explode_cmd(self): """[internal] find the appropriate command to open the archive""" raise NotImplementedError("ArchiveParser: abstract base class method '_explode_cmd'") def _explode(self): """[internal] Explode a archive for neutral parsing""" cmd = self._explode_cmd() assert self._archive is not None # assigned in _copy_archive assert self._archive_dir is not None # assigned in _explode_cmd if cmd: _my_popen(cmd) if os.path.isdir(self._archive_dir): return raise InvalidArchiveError("Archive did not expand to %s" % self._archive_dir) raise InvalidArchiveError("Could not find command to open archive: %s" % self._archive) # private helper methods --------------------------------------------- def _find(self, filename): """[internal] Returns the relative path to a file in the archive""" file_path = None contents = os.listdir(self._archive_dir) while contents: entry = contents.pop() path = os.path.join(self._archive_dir, entry) if os.path.isdir(path): p_contents = os.listdir(path) e_contents = [os.path.join(entry, e) for e in p_contents] # this really is something of a hack, the newest contents will # 'prepended' to the queue instead of 'appended' changing the # search into depth-first when I think breadth-first would be # the expected behavior # that's what we get for programming in python which doesn't # supply a nice way of adding real data-structure support # I already tried extending e_contents with contents and then # reassigning the contents reference to e_contents, but the # damn while loop still had a hold of the original reference contents.extend(e_contents) else: if entry.endswith(filename): file_path = entry break else: # if __degug__: sys.stderr.write("[_find] '%s' not found\n" % file) pass return file_path # public api --------------------------------------------------------- def list(self, prefix=""): """Return a tuple of directories and files in the archive at the given directory: prefix""" dirname = os.path.join(self._archive_dir, prefix) assert os.path.isdir(dirname) l = os.listdir(dirname) d = [] f = [] for i in l: if os.path.isdir(os.path.join(dirname, i)): d.append(i) else: f.append(i) return (d, f) def contains(self, filename): """Returns true iff the file is contained in the archive""" return self._find(filename) is not None def read(self, filename): """Returns the contents of the file, or None on error First occurence of that file in archive is returned """ f = self._find(filename) if f: return self.direct_read(f) return None def direct_read(self, filename): """ Returns the contens of the file, file is relative path in archive. Top most level (_get_archive_dir) is automaticaly added. """ # pylint: disable=W0703 f = os.path.join(os.path.abspath(self._archive_dir), filename) contents = None if os.path.isfile(f) and os.access(f, os.R_OK): try: fd = open(f) contents = fd.read() fd.close() except Exception: contents = None return contents def zip(self, prefix=""): """Create a zip archive of a (sub-)directory of the archive""" dirname = os.path.join(self._archive_dir, prefix) zip_dir = os.path.basename(dirname) parent_dir = os.path.dirname(dirname) cwd = os.getcwd() os.chdir(parent_dir) zip_file = os.path.join(self._parent_dir, "%s.zip" % zip_dir) fd = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED) for base, _dirs, files in os.walk(zip_dir): fd.write(base) for f in files: fd.write(os.path.join(base, f)) os.chdir(cwd) if os.path.isfile(zip_file): return zip_file return None def cpio(self, prefix): """Create a cpio archive of a (sub-)directory of the archive""" cpio_file = os.path.join(self._temp_dir, "%s.pkg" % prefix) cmd = "pkgtrans -s %s %s %s" % (self._archive_dir, cpio_file, prefix) _my_popen(cmd) if os.path.isfile(cpio_file): return cpio_file return None # parser for zip archives ------------------------------------------------ class ZipParser(ArchiveParser): def __init__(self, archive, tempdir="/tmp/"): self.zip_file = zipfile.ZipFile(archive, 'r') ArchiveParser.__init__(self, archive, tempdir) def _get_archive_dir(self): return self.zip_file.namelist()[0] def _explode(self): """Explode zip archive""" self._archive_dir = os.path.join(self._temp_dir, self._get_archive_dir()).rstrip('/') try: self.zip_file.extractall(self._temp_dir) except Exception: e = sys.exc_info()[1] raise InvalidArchiveError("Archive did not expand to %s: %s" % (self._archive_dir, str(e))) return def _explode_cmd(self): pass # parser for tar archives ------------------------------------------------ class TarParser(ArchiveParser): def __init__(self, archive, tempdir="/tmp/"): self.tar_file = tarfile.open(archive, 'r') ArchiveParser.__init__(self, archive, tempdir) def _get_archive_dir(self): return self.tar_file.getnames()[0] def _explode(self): """Explode tar archive""" self._archive_dir = os.path.join(self._temp_dir, self._get_archive_dir()) try: self.tar_file.extractall(path=self._temp_dir) except Exception: e = sys.exc_info()[1] raise InvalidArchiveError("Archive did not expand to %s: %s" % (self._archive_dir, str(e))) return def _explode_cmd(self): pass # parser for cpio archives ----------------------------------------------- class CpioParser(ArchiveParser): def _get_archive_dir(self): return os.path.basename(self._archive)[0:5] # arbitrary def _explode_cmd(self): """Return the appropriate command for exploding a cpio archive""" self._archive_dir = os.path.join(self._temp_dir, self._get_archive_dir()) if not _has_executable("pkgtrans"): raise ArchiveException("cannot open %s, 'pkgtrans' not found" % self._archive) return "cd %s; mkdir %s; pkgtrans %s %s all" % \ (self._temp_dir, self._archive_dir, self._archive, self._archive_dir) # internal helper methods ------------------------------------------------ def _has_executable(exc): """Return true if the executable is found in the $PATH""" # flag the error condition, this will evaluate to False if "PATH" not in os.environ: return None # this is posix specific dirs = os.environ["PATH"].split(':') for dirname in dirs: path = os.path.join(dirname, exc) if os.access(path, os.X_OK): return True return False def _my_popen(cmd): """Execute a command as a subprocess and return its exit status""" # pylint: disable=E1101 popen = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True) popen.stdin.close() txt = "" while 1: rd, _wr, ex = select.select([popen.stdout, popen.stderr], [], [popen.stdout, popen.stderr], 5) if ex: txt += popen.stdout.read() txt += popen.stderr.read() break if rd: txt += rd[0].read() break status = popen.wait() if status != 0: raise Exception("%s exited with status %s and error\n%s" % (cmd, status, txt)) return # NOTE these next two functions rely on file magic to determine the compression # and archive types. some file magic information can be found here: # http://www.astro.keele.ac.uk/oldusers/rno/Computing/File_magic.html def _decompress(archive): """[internal] Decompress compressed archives and return the new archive name""" cmd = "" sfx_list = None # determine which type of compression we're dealing with, if any fd = open(archive, 'r') magic = fd.read(2) fd.close() if magic == "BZ": cmd = "bunzip2" sfx_list = (".bz2", ".bz") elif magic == "\x1F\x9D": cmd = "uncompress" sfx_list = (".Z",) elif magic == "\x1F\x8B": cmd = "gunzip" sfx_list = (".gz",) # decompress the archive if it is compressed if cmd: if not _has_executable(cmd): raise ArchiveException("Cannot decompress %s, '%s' not found" % (archive, cmd)) print("Decompressing archive") _my_popen("%s %s" % (cmd, archive)) # remove the now invalid suffix from the archive name for sfx in sfx_list: if archive[-len(sfx):] == sfx: archive = archive[:-len(sfx)] break return archive # archive parser factory ------------------------------------------------- def get_archive_parser(archive, tempdir="/tmp/"): """Factory function that returns an ArchiveParser object for the given archive""" # decompress the archive archive = _decompress(archive) parserClass = None fd = open(archive, 'r') magic = fd.read(4) if magic == "PK\x03\x04": parserClass = ZipParser fd.seek(0) magic = fd.read(20) if magic == "# PaCkAgE DaTaStReAm": parserClass = CpioParser fd.seek(257) magic = fd.read(5) if magic == "ustar": parserClass = TarParser # pre-posix tar doesn't have any standard file magic if archive.endswith(".tar"): parserClass = TarParser fd.close() if parserClass is None: raise UnknownArchiveError("Wasn't able to identify: '%s'" % archive) return parserClass(archive, tempdir) 07070100000007000081B400000000000000000000000161FA7D4F000028FC000000000000000000000000000000000000001700000000mgr-push/connection.py# # Copyright (c) 2008--2017 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # import socket import base64 import sys # pylint: disable=F0401,E0611,W0632 from rhn import connections, rpclib from uyuni.common.usix import ListType, TupleType, IntType, raise_with_tb from uyuni.common.rhn_pkg import InvalidPackageError, package_from_filename from rhnpush.utils import tupleify_urlparse if sys.version_info[0] == 3: from urllib.parse import splitport from urllib.parse import urlparse else: from urlparse import urlparse from urllib import splitport # pylint: disable=C0412 # pylint: disable=W0622 class ConnectionError(Exception): pass # pylint: disable=R0902 class BaseConnection: def __init__(self, uri, proxy=None): self._scheme, (self._host, self._port), self._path = parse_url(uri)[:3] if proxy: arr = rpclib.get_proxy_info(proxy) self._proxy_host = arr[0] self._proxy_port = arr[1] self._proxy_username = arr[2] self._proxy_password = arr[3] else: self._proxy_host = None self._trusted_certs = None self._connection = None self._timeout = None def set_timeout(self, timeout): self._timeout = timeout def get_connection(self): if self._scheme not in ['http', 'https']: raise ValueError("Unsupported scheme", self._scheme) params = {} if self._timeout is not None: params['timeout'] = self._timeout if self._proxy_host: params.update({ 'host': self._host, 'port': self._port, 'proxy': "%s:%s" % (self._proxy_host, self._proxy_port), 'username': self._proxy_username, 'password': self._proxy_password, }) if self._scheme == 'http': return connections.HTTPProxyConnection(**params) params['trusted_certs'] = self._trusted_certs return connections.HTTPSProxyConnection(**params) else: if self._scheme == 'http': return connections.HTTPConnection(self._host, self._port, **params) params['trusted_certs'] = self._trusted_certs return connections.HTTPSConnection(self._host, self._port, **params) def connect(self): self._connection = self.get_connection() self._connection.connect() def putrequest(self, method, url=None, skip_host=0): if url is None: url = self._path return self._connection.putrequest(method, url=url, skip_host=skip_host) def __getattr__(self, name): return getattr(self._connection, name) class PackageUpload: header_prefix = "X-RHN-Upload" user_agent = "rhn-package-upload" def __init__(self, url, proxy=None): self.connection = BaseConnection(url, proxy) self.headers = {} self.package_name = None self.package_epoch = None self.package_version = None self.package_release = None self.package_arch = None self.checksum = None self.checksum_type = None self.nvra = None self._resp_headers = None self.packaging = None self._response = None def set_header(self, name, value): if name not in self.headers: vlist = self.headers[name] = [] else: vlist = self.headers[name] if not isinstance(vlist, (ListType, TupleType)): vlist = [vlist] vlist.append(value) def send_http_headers(self, method, content_length=None): try: self.connection.connect() except socket.error: e = sys.exc_info()[1] raise_with_tb(ConnectionError("Error connecting", str(e)), sys.exc_info()[2]) # Add content_length if 'Content-Length' not in self.headers and \ content_length is not None: self.set_header('Content-Length', content_length) self.connection.putrequest(method) # Additional headers for hname, hval in self.headers.items(): if not isinstance(hval, (ListType, TupleType)): hval = [hval] for v in hval: self.connection.putheader(str(hname), str(v)) self.connection.endheaders() def send_http_body(self, stream_body): if stream_body is None: return stream_body.seek(0, 0) buffer_size = 16384 while 1: buf = stream_body.read(buffer_size) if not buf: break try: self.connection.send(buf) except IOError: e = sys.exc_info()[1] raise_with_tb(ConnectionError("Error sending body", str(e)), sys.exc_info()[2]) def send_http(self, method, stream_body=None): if stream_body is None: content_length = 0 else: stream_body.seek(0, 2) content_length = stream_body.tell() self.send_http_headers(method, content_length=content_length) self.send_http_body(stream_body) self._response = self.connection.getresponse() self._resp_headers = self._response.msg return self._response def upload(self, filename, fileChecksumType, fileChecksum): """ Uploads a file. Returns (http_error_code, error_message) Sets: self.package_name self.package_epoch self.package_version self.package_release self.package_arch """ try: a_pkg = package_from_filename(filename) a_pkg.read_header() except InvalidPackageError: return -1, "Not an RPM: %s" % filename # Set some package data members self.package_name = a_pkg.header['name'] self.package_epoch = a_pkg.header['epoch'] self.package_version = a_pkg.header['version'] self.package_release = a_pkg.header['release'] if a_pkg.header.is_source: if 1051 in a_pkg.header.keys(): self.package_arch = 'nosrc' else: self.package_arch = 'src' else: self.package_arch = a_pkg.header['arch'] self.packaging = a_pkg.header.packaging nvra = [self.package_name, self.package_version, self.package_release, self.package_arch] if isinstance(nvra[3], IntType): # Old rpm format return -1, "Deprecated RPM format: %s" % filename self.nvra = nvra # use the precomputed passed checksum self.checksum_type = fileChecksumType self.checksum = fileChecksum # Set headers self.set_header("Content-Type", "application/x-rpm") self.set_header("User-Agent", self.user_agent) # Custom RHN headers prefix = self.header_prefix self.set_header("%s-%s" % (prefix, "Package-Name"), nvra[0]) self.set_header("%s-%s" % (prefix, "Package-Version"), nvra[1]) self.set_header("%s-%s" % (prefix, "Package-Release"), nvra[2]) self.set_header("%s-%s" % (prefix, "Package-Arch"), nvra[3]) self.set_header("%s-%s" % (prefix, "Packaging"), self.packaging) if self.checksum_type == 'md5': self.set_header("%s-%s" % (prefix, "File-MD5sum"), self.checksum) else: self.set_header("%s-%s" % (prefix, "File-Checksum-Type"), self.checksum_type) self.set_header("%s-%s" % (prefix, "File-Checksum"), self.checksum) a_pkg.input_stream.seek(0, 0) self._response = self.send_http('POST', stream_body=a_pkg.input_stream) a_pkg.input_stream.close() retval = self.process_response() self.connection.close() return retval def process_response(self): status = self._response.status reason = self._response.reason if status == 200: # OK return status, "OK" if status == 201: # Created return (status, "%s %s: %s-%s-%s.%s.rpm already uploaded" % ( self.checksum_type, self.checksum, self.nvra[0], self.nvra[1], self.nvra[2], self.nvra[3])) if status in (404, 409): # Conflict errstring = self.get_error_message(self._resp_headers) return status, errstring data = self._response.read() if status == 403: # In this case Authentication is no longer valid on server # client needs to re-authenticate itself. errstring = self.get_error_message(self._resp_headers) return status, errstring if status == 500: print("Internal server error", status, reason) errstring = self.get_error_message(self._resp_headers) return status, data + errstring return status, data def get_error_message(self, headers): prefix = self.header_prefix + '-Error' text = [x[1] for x in headers.getaddrlist(prefix + '-String')] # text is a list now, convert it to a string text = '\n'.join(text) # pylint: disable=W1505 text = base64.decodestring(text) return text def parse_url(url, scheme="http", path='/'): _scheme, netloc, _path, params, query, fragment = tupleify_urlparse( urlparse(url)) if not netloc: # No scheme - trying to patch it up ourselves? url = scheme + "://" + url _scheme, netloc, _path, params, query, fragment = tupleify_urlparse( urlparse(url)) if not netloc: # XXX raise Exception() (host, port) = splitport(netloc) if not _path: _path = path return (_scheme, (host, port), _path, params, query, fragment) 07070100000008000081B400000000000000000000000161FA7D4F0000003C000000000000000000000000000000000000001C00000000mgr-push/mgr-push-rpmlintrcaddFilter("suse-filelist-forbidden-sysconfig .*/sysconfig") 07070100000009000081B400000000000000000000000161FA7D4F000024B4000000000000000000000000000000000000001A00000000mgr-push/mgr-push.changes------------------------------------------------------------------- Wed Feb 02 13:45:55 CET 2022 - jmassaguerpla@suse.de - version 4.2.4-1 * Do not build python 2 package for SLE15SP4 and higher ------------------------------------------------------------------- Fri Jul 16 14:06:48 CEST 2021 - jgonzalez@suse.com - version 4.2.3-1 - Adapt the tests to the new images ------------------------------------------------------------------- Thu Dec 03 13:38:22 CET 2020 - jgonzalez@suse.com - version 4.2.2-1 - defined __python for python2. - Excluded RHEL8 for Python 2 build. ------------------------------------------------------------------- Fri Sep 18 11:14:23 CEST 2020 - jgonzalez@suse.com - version 4.2.1-1 - Update package version to 4.2.0 ------------------------------------------------------------------- Wed Nov 27 16:42:09 CET 2019 - jgonzalez@suse.com - version 4.1.1-1 - replace spacewalk-usix and spacewalk-backend-libs with uyuni-common-libs - Bump version to 4.1.0 (bsc#1154940) ------------------------------------------------------------------- Wed May 15 20:08:24 CEST 2019 - jgonzalez@suse.com - version 4.0.6-1 - Obsolete newer versions of rhnpush ------------------------------------------------------------------- Wed May 15 15:00:24 CEST 2019 - jgonzalez@suse.com - version 4.0.5-1 - SPEC cleanup - Fix wrong bugzilla entry at changelog ------------------------------------------------------------------- Mon Apr 22 12:02:26 CEST 2019 - jgonzalez@suse.com - version 4.0.4-1 - add makefile and configuration for pylint ------------------------------------------------------------------- Wed Jan 16 12:16:04 CET 2019 - jgonzalez@suse.com - version 4.0.3-1 - Fix dependencies to spacewalk-backend-libs ------------------------------------------------------------------- Fri Oct 26 09:56:12 CEST 2018 - jgonzalez@suse.com - version 4.0.2-1 - Add Uyuni URL to package ------------------------------------------------------------------- Fri Aug 10 16:02:45 CEST 2018 - jgonzalez@suse.com - version 4.0.1-1 - Bump version to 4.0.0 (bsc#1104034) - Rename package to mgr-push to allow version 4.0.0 (bsc#1104034) - Fix copyright for the package specfile (bsc#1103696) ------------------------------------------------------------------- Mon Apr 23 08:53:06 CEST 2018 - jgonzalez@suse.com - version 5.5.113.2-1 - Sync with upstream (bsc#1083294) - rhnpush is needed on python2 due to spacewalk-proxy ------------------------------------------------------------------- Mon Mar 26 08:40:29 CEST 2018 - jgonzalez@suse.com - version 5.5.113.1-1 - Sync with upstream (bsc#1083294) - Build python2 on SUSE ------------------------------------------------------------------- Mon Mar 05 09:16:14 CET 2018 - jgonzalez@suse.com - version 5.5.111.2-1 - remove empty clean section from spec (bsc#1083294) ------------------------------------------------------------------- Fri Feb 23 10:48:18 CET 2018 - jgonzalez@suse.com - version 5.5.111.1-1 - Sync with upstream ------------------------------------------------------------------- Thu Oct 26 17:16:51 CEST 2017 - mc@suse.de - version 5.5.108.1-1 - move rhnpush files into proper python2/python3 subpackages ------------------------------------------------------------------- Wed Aug 09 12:11:07 CEST 2017 - fkobzik@suse.de - version 5.5.104.3-1 - disable pylint check during build also for RHEL7 ------------------------------------------------------------------- Mon May 29 15:40:35 CEST 2017 - mc@suse.de - version 5.5.104.2-1 - fixed pylint warnings disabled python3 pylint on Fedora 26+ for now - removed outdated solaris2mpm script ------------------------------------------------------------------- Fri Mar 31 09:59:14 CEST 2017 - mc@suse.de - version 5.5.104.1-1 - Pylint fixes in rhnpush ------------------------------------------------------------------- Tue Mar 07 16:00:37 CET 2017 - mc@suse.de - version 5.5.102.1-1 - Updated links to github in spec files - require spacewalk-usix indead of spacewalk-backend-usix ------------------------------------------------------------------- Wed Jan 11 15:34:37 CET 2017 - michele.bologna@suse.com - version 5.5.101.1-1 - Version 5.5.101-1 ------------------------------------------------------------------- Wed Apr 06 08:49:39 CEST 2016 - mc@suse.de - version 5.5.91.2-1 - don't count on having newest rhn-client-tools ------------------------------------------------------------------- Sat Jan 16 11:29:10 CET 2016 - mc@suse.de - version 5.5.91.1-1 - alow to use existing rpcServer when creating RhnServer ------------------------------------------------------------------- Mon Nov 30 10:59:38 CET 2015 - mc@suse.de - version 5.5.90.1-1 - rhn-satellite-activate: manual references removed ------------------------------------------------------------------- Wed Oct 07 15:56:48 CEST 2015 - mc@suse.de - version 5.5.89.1-1 - Wire in timeout for rhnpush ------------------------------------------------------------------- Mon Jun 22 15:30:09 CEST 2015 - jrenner@suse.de - version 5.5.71.8-1 - fix --ca-chain option for rhnpush (bsc#931503, bsc#895869) ------------------------------------------------------------------- Tue Feb 03 13:08:35 CET 2015 - mc@suse.de - version 5.5.71.7-1 - Getting rid of Tabs and trailing spaces ------------------------------------------------------------------- Thu Dec 18 13:33:42 CET 2014 - mc@suse.de - version 5.5.71.6-1 - rhnpush: fix pylint checks and code quality ------------------------------------------------------------------- Wed Sep 3 01:41:38 CEST 2014 - ro@suse.de - sanitize release line in specfile ------------------------------------------------------------------- Fri Jun 13 16:19:07 CEST 2014 - jrenner@suse.de - version 5.5.71.5-1 - Add default path structure to proxy lookaside that avoids collisions - Make rhnpush backwards-compatible with old spacewalk-proxy ------------------------------------------------------------------- Thu Apr 10 17:32:20 CEST 2014 - mc@suse.de - version 5.5.71.4-1 ------------------------------------------------------------------- Thu Apr 10 17:32:04 CEST 2014 - mc@suse.de - fix release in specfile for SLE12 (bnc#872970) ------------------------------------------------------------------- Thu Mar 27 10:07:33 CET 2014 - fcastelli@suse.com - version 5.5.71.3-1 - correcting exception type ------------------------------------------------------------------- Thu Feb 13 15:03:40 CET 2014 - mc@suse.de - version 5.5.71.2-1 - use SUSE package name in requires - pylint check only on SLE11 ------------------------------------------------------------------- Tue Dec 10 14:11:22 CET 2013 - mc@suse.de - version 5.5.71.1-1 - switch to 2.1 ------------------------------------------------------------------- Thu Aug 02 16:23:54 CEST 2012 - mc@suse.de - version 5.5.42.7-1 - code cleanup - fixed man page - removed dead --no-cache option - fixed --no-session-caching option ------------------------------------------------------------------- Mon Jul 16 15:17:44 CEST 2012 - ug@suse.de - version 5.5.42.6-1 - solaris2mpm on RHEL5 is not supported - solaris2mpm needs zipfile with ZIP64 extension - remove trailing '/' from from archive dir - reuse UploadError from uploadLib - simplified authentication code ------------------------------------------------------------------- Mon Jun 25 10:22:27 CEST 2012 - mc@suse.de - version 5.5.42.5-1 - removed commented out code and obsoleted comments ------------------------------------------------------------------- Thu Jun 21 10:52:17 CEST 2012 - jrenner@suse.de - version 5.5.42.4-1 - add COPYING file (bnc#764854) ------------------------------------------------------------------- Thu May 31 10:55:28 CEST 2012 - mc@suse.de - version 5.5.42.3-1 - Use the correct a_pkg variable. ------------------------------------------------------------------- Fri Apr 27 16:54:01 CEST 2012 - mc@suse.de - version 5.5.42.2-1 - removed unused get_header_struct_size() - removed unused function get_header_byte_range() ------------------------------------------------------------------- Wed Mar 21 17:35:17 CET 2012 - mc@suse.de - version 5.5.42.1-1 - Bumping package version ------------------------------------------------------------------- Tue Feb 22 16:23:06 CET 2011 - mantel@suse.de - add missing "import string" (bnc#672643) ------------------------------------------------------------------- Thu Feb 17 14:06:01 CET 2011 - mantel@suse.de - fix build: create symlink only after binary has been installed ------------------------------------------------------------------- Thu Feb 17 13:39:50 CET 2011 - mantel@suse.de - rename "Red Hat Network" to "SUSE Manager" for SUSE (bnc#672643) - add mgrpush symlink (bnc#672643) ------------------------------------------------------------------- Tue Feb 1 16:54:39 CET 2011 - mc@suse.de - fix build for RH4 ------------------------------------------------------------------- Tue Jan 11 16:39:04 CET 2011 - mc@suse.de - tag strings which needs changes - fix Requires line in spec ------------------------------------------------------------------- Wed Sep 15 12:16:32 CEST 2010 - mantel@suse.de - fix post-build-checks ------------------------------------------------------------------- Tue Sep 14 17:59:47 CEST 2010 - mantel@suse.de - Initial release of rhnpush ------------------------------------------------------------------- 0707010000000A000081B400000000000000000000000161FA7D4F000015B4000000000000000000000000000000000000001700000000mgr-push/mgr-push.spec# # spec file for package mgr-push # # Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2008-2018 Red Hat, Inc. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via https://bugs.opensuse.org/ # # Old name and version+1 before renaming to mgr-push %define oldname rhnpush %define oldversion 5.5.114 %{!?pylint_check: %global pylint_check 0} %global __python /usr/bin/python2 %if 0%{?fedora} || 0%{?suse_version} > 1320 || 0%{?rhel} >= 8 %global build_py3 1 %global default_py3 1 %endif %if !( 0%{?rhel} >= 8 || 0%{?sle_version} >= 150400 ) %global build_py2 1 %endif %define pythonX %{?default_py3: python3}%{!?default_py3: python2} Name: mgr-push Summary: Package uploader for the Spacewalk License: GPL-2.0-only Group: Applications/System Url: https://github.com/uyuni-project/uyuni Version: 4.2.4 Provides: %{oldname} = %{oldversion} Obsoletes: %{oldname} < %{oldversion} Release: 1%{?dist} Source0: https://github.com/spacewalkproject/spacewalk/archive/%{name}-%{version}.tar.gz Source1: %{name}-rpmlintrc BuildRoot: %{_tmppath}/%{name}-%{version}-build %if 0%{?fedora} || 0%{?rhel} || 0%{?suse_version} >= 1210 BuildArch: noarch %endif Requires: %{pythonX}-%{name} = %{version}-%{release} BuildRequires: docbook-utils BuildRequires: gettext %if 0%{?pylint_check} %if 0%{?build_py2} BuildRequires: spacewalk-python2-pylint %endif %if 0%{?build_py3} BuildRequires: spacewalk-python3-pylint %endif %endif %description rhnpush uploads package headers to the Spacewalk servers into specified channels and allows for several other channel management operations relevant to controlling what packages are available per channel. %if 0%{?build_py2} %package -n python2-%{name} Summary: Package uploader for the Spacewalk or Red Hat Satellite Server Group: Applications/System Provides: python2-%{oldname} = %{oldversion} Obsoletes: python2-%{oldname} < %{oldversion} Requires: %{name} = %{version}-%{release} %if 0%{?fedora} >= 28 Requires: python2-rpm BuildRequires: python2-devel %else Requires: rpm-python BuildRequires: python-devel %endif Requires: python2-rhn-client-tools Requires: rhnlib >= 2.8.3 Requires: python2-uyuni-common-libs BuildRequires: python2-rhn-client-tools BuildRequires: python2-uyuni-common-libs %description -n python2-%{name} Python 2 specific files for rhnpush. %endif %if 0%{?build_py3} %package -n python3-%{name} Summary: Package uploader for the Spacewalk or Red Hat Satellite Server Group: Applications/System Provides: python3-%{oldname} = %{oldversion} Obsoletes: python3-%{oldname} < %{oldversion} Requires: %{name} = %{version}-%{release} %if 0%{?suse_version} Requires: python3-rpm %else Requires: rpm-python3 %endif Requires: python3-rhn-client-tools Requires: python3-rhnlib >= 2.8.3 Requires: python3-uyuni-common-libs BuildRequires: python3-devel BuildRequires: python3-rhn-client-tools BuildRequires: python3-rpm-macros BuildRequires: python3-uyuni-common-libs %description -n python3-%{name} Python 3 specific files for rhnpush. %endif %prep %setup -q %build make -f Makefile.rhnpush all %install install -d $RPM_BUILD_ROOT/%{python_sitelib} %if 0%{?build_py2} make -f Makefile.rhnpush install PREFIX=$RPM_BUILD_ROOT ROOT=%{python_sitelib} \ MANDIR=%{_mandir} PYTHON_VERSION=%{python_version} %endif %if 0%{?build_py3} sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|' rhnpush install -d $RPM_BUILD_ROOT/%{python3_sitelib} make -f Makefile.rhnpush install PREFIX=$RPM_BUILD_ROOT ROOT=%{python3_sitelib} \ MANDIR=%{_mandir} PYTHON_VERSION=%{python3_version} %endif %define default_suffix %{?default_py3:-%{python3_version}}%{!?default_py3:-%{python_version}} ln -s rhnpush%{default_suffix} $RPM_BUILD_ROOT%{_bindir}/rhnpush %if 0%{?suse_version} ln -s rhnpush $RPM_BUILD_ROOT/%{_bindir}/mgrpush %endif %check %if 0%{?pylint_check} # check coding style %if 0%{?build_py2} export PYTHONPATH=$RPM_BUILD_ROOT%{python_sitelib} spacewalk-python2-pylint $RPM_BUILD_ROOT%{_bindir} $RPM_BUILD_ROOT%{python_sitelib} %endif %if 0%{?build_py3} export PYTHONPATH=$RPM_BUILD_ROOT%{python3_sitelib} spacewalk-python3-pylint $RPM_BUILD_ROOT%{_bindir} $RPM_BUILD_ROOT%{python3_sitelib} %endif %endif %files %defattr(-,root,root) %{_bindir}/rhnpush %{_bindir}/rpm2mpm %dir %{_sysconfdir}/sysconfig/rhn %if 0%{?suse_version} %{_bindir}/mgrpush %endif %config(noreplace) %attr(644,root,root) %{_sysconfdir}/sysconfig/rhn/rhnpushrc %{_mandir}/man8/rhnpush.8* %doc COPYING %if 0%{?build_py2} %files -n python2-%{name} %defattr(-,root,root) %attr(755,root,root) %{_bindir}/rhnpush-%{python_version} %{python_sitelib}/rhnpush/ %endif %if 0%{?build_py3} %files -n python3-%{name} %defattr(-,root,root) %attr(755,root,root) %{_bindir}/rhnpush-%{python3_version} %{python3_sitelib}/rhnpush/ %endif %changelog 0707010000000B000041FD00000000000000000000000161FA7D4F00000000000000000000000000000000000000000000001100000000mgr-push/patches0707010000000C000081B400000000000000000000000161FA7D4F000001FB000000000000000000000000000000000000002200000000mgr-push/patches/rhel4-static.dif--- solaris2mpm.py +++ solaris2mpm.py @@ -30,7 +30,6 @@ try: except ImportError: import md5 class hashlib: - @staticmethod def new(checksum): # Add sha1 if needed. if checksum == 'md5': @@ -39,6 +38,8 @@ except ImportError: if checksum not in ['md5', 'sha1']: raise ValueError, "Incompatible checksum type" + new = staticmethod(new) + from spacewalk.common import rhn_mpm from archive import get_archive_parser 0707010000000D000081B400000000000000000000000161FA7D4F00001396000000000000000000000000000000000000001200000000mgr-push/pylintrc# mgr-push package pylint configuration [MASTER] # Profiled execution. profile=no # Pickle collected data for later comparisons. persistent=no [MESSAGES CONTROL] # Disable the message(s) with the given id(s). disable=I0011, C0302, C0111, R0801, R0902, R0903, R0904, R0912, R0913, R0914, R0915, R0921, R0922, W0142, W0403, W0603, C1001, W0121, useless-else-on-loop, bad-whitespace, unpacking-non-sequence, superfluous-parens, cyclic-import, redefined-variable-type, no-else-return, # Uyuni disabled E0203, E0611, E1101, E1102 # list of disabled messages: #I0011: 62: Locally disabling R0201 #C0302: 1: Too many lines in module (2425) #C0111: 1: Missing docstring #R0902: 19:RequestedChannels: Too many instance attributes (9/7) #R0903: Too few public methods #R0904: 26:Transport: Too many public methods (22/20) #R0912:171:set_slots_from_cert: Too many branches (59/20) #R0913:101:GETServer.__init__: Too many arguments (11/10) #R0914:171:set_slots_from_cert: Too many local variables (38/20) #R0915:171:set_slots_from_cert: Too many statements (169/50) #W0142:228:MPM_Package.write: Used * or ** magic #W0403: 28: Relative import 'rhnLog', should be 'backend.common.rhnLog' #W0603: 72:initLOG: Using the global statement # for pylint-1.0 we also disable #C1001: 46, 0: Old-style class defined. (old-style-class) #W0121: 33,16: Use raise ErrorClass(args) instead of raise ErrorClass, args. (old-raise-syntax) #W:243, 8: Else clause on loop without a break statement (useless-else-on-loop) # pylint-1.1 checks #C:334, 0: No space allowed after bracket (bad-whitespace) #W:162, 8: Attempting to unpack a non-sequence defined at line 6 of (unpacking-non-sequence) #C: 37, 0: Unnecessary parens after 'not' keyword (superfluous-parens) #C:301, 0: Unnecessary parens after 'if' keyword (superfluous-parens) [REPORTS] # Set the output format. Available formats are text, parseable, colorized, msvs # (visual studio) and html output-format=parseable # Include message's id in output include-ids=yes # Tells whether to display a full report or only the messages reports=yes # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" [VARIABLES] # A regular expression matching names used for dummy variables (i.e. not used). dummy-variables-rgx=_|dummy [BASIC] # Regular expression which should only match correct module names #module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ module-rgx=([a-zA-Z_][a-zA-Z0-9_]+)$ # Regular expression which should only match correct module level names const-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$ # Regular expression which should only match correct class names class-rgx=[a-zA-Z_][a-zA-Z0-9_]+$ # Regular expression which should only match correct function names function-rgx=[a-z_][a-zA-Z0-9_]{,42}$ # Regular expression which should only match correct method names method-rgx=[a-z_][a-zA-Z0-9_]{,42}$ # Regular expression which should only match correct instance attribute names attr-rgx=[a-z_][a-zA-Z0-9_]{,30}$ # Regular expression which should only match correct argument names argument-rgx=[a-z_][a-zA-Z0-9_]{,30}$ # Regular expression which should only match correct variable names variable-rgx=[a-z_][a-zA-Z0-9_]{,30}$ # Regular expression which should only match correct list comprehension / # generator expression variable names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ # Regular expression which should only match correct class sttribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,42}|(__.*__))$ # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata # List of builtins function names that should not be used, separated by a comma bad-functions=apply,input [DESIGN] # Maximum number of arguments for function / method max-args=10 # Maximum number of locals for function / method body max-locals=20 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of branch for function / method body max-branchs=20 # Maximum number of statements in function / method body max-statements=50 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Minimum number of public methods for a class (see R0903). min-public-methods=1 # Maximum number of public methods for a class (see R0904). max-public-methods=20 [CLASSES] [FORMAT] # Maximum number of characters on a single line. max-line-length=120 # Maximum number of lines in a module max-module-lines=1000 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes= 0707010000000E000081FD00000000000000000000000161FA7D4F000002E6000000000000000000000000000000000000001100000000mgr-push/rhnpush#!/usr/bin/python # # Wrapper for rhnpush_main.py # import sys try: from rhnpush import rhnpush_main except ImportError: e = sys.exc_info()[1] sys.stderr.write("Unable to load module rhnpush_main\n") sys.stderr.write(str(e) + "\n") sys.exit(1) if __name__ == '__main__': try: sys.exit(rhnpush_main.main() or 0) except KeyboardInterrupt: e = sys.exc_info()[1] sys.stderr.write("\nUser interrupted process.\n") sys.exit(0) except SystemExit: e = sys.exc_info()[1] sys.exit(e.code) # pylint: disable=W0703 except Exception: e = sys.exc_info()[1] sys.stderr.write("\nERROR: unhandled exception occurred: (%s).\n" % e) sys.exit(-1) 0707010000000F000081B400000000000000000000000161FA7D4F0000329F000000000000000000000000000000000000001600000000mgr-push/rhnpush.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [ <!ENTITY RHNSAT "Spacewalk Server" > <!ENTITY RHNPUSH "Spacewalk Package Pusher" > ]> <refentry> <RefMeta> <RefEntryTitle>rhnpush</RefEntryTitle><manvolnum>8</manvolnum> <RefMiscInfo>Version 2.0</RefMiscInfo> </RefMeta> <RefNameDiv> <RefName><command>rhnpush</command></RefName> <RefPurpose> Utility to push binary and source RPMs into an RHN channel. </RefPurpose> </RefNameDiv> <RefSynopsisDiv> <Synopsis> <cmdsynopsis> <command>rhnpush</command> <arg>options <replaceable>...</replaceable></arg> <arg>-v</arg> <arg>--verbose</arg> </cmdsynopsis> <cmdsynopsis> <arg>-d<replaceable>DIRECTORY</replaceable></arg> <arg>--dir=<replaceable>DIRECTORY</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-c<replaceable>CHANNEL_LABEL</replaceable></arg> <arg>--channel=<replaceable>CHANNEL_LABEL</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-n<replaceable>N_HEADERS_PER_CALL</replaceable></arg> <arg>--count=<replaceable>N_HEADERS_PER_CALL</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-l</arg> <arg>--list</arg> </cmdsynopsis> <cmdsynopsis> <arg>-r<replaceable>RELATIVE_DIRECTORY</replaceable></arg> <arg>--reldir=<replaceable>RELATIVE_DIRECTORY</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-o<replaceable>ORGANIZATION_ID</replaceable></arg> <arg>--orgid=<replaceable>ORGANIZATION_ID</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-u<replaceable>USERNAME</replaceable></arg> <arg>--username=<replaceable>USERNAME</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-p<replaceable>PASSWORD</replaceable></arg> <arg>--password=<replaceable>PASSWORD</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-s</arg> <arg>--stdin</arg> </cmdsynopsis> <cmdsynopsis> <arg>-X<replaceable>glob</replaceable></arg> <arg>--exclude=<replaceable>GLOB</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>--force</arg> </cmdsynopsis> <cmdsynopsis> <arg>--nosig</arg> </cmdsynopsis> <cmdsynopsis> <arg>--newest</arg> </cmdsynopsis> <cmdsynopsis> <arg>--nullorg</arg> </cmdsynopsis> <cmdsynopsis> <arg>--header</arg> </cmdsynopsis> <cmdsynopsis> <arg>--source</arg> </cmdsynopsis> <cmdsynopsis> <arg>--server=<replaceable>SERVER</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>--proxy=<replaceable>PROXY:PORT</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-N</arg> <arg>--new-cache</arg> </cmdsynopsis> <cmdsynopsis> <arg>--no-session-caching</arg> </cmdsynopsis> <cmdsynopsis> <arg>--extended-test</arg> </cmdsynopsis> <cmdsynopsis> <arg>--test</arg> </cmdsynopsis> <cmdsynopsis> <arg>--tolerant</arg> </cmdsynopsis> <cmdsynopsis> <arg>--timeout=<replaceable>SECONDS</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <arg>-h</arg> <arg>--help</arg> </cmdsynopsis> <cmdsynopsis> <arg>-?</arg> <arg>--usage</arg> </cmdsynopsis> </Synopsis> </RefSynopsisDiv> <RefSect1><Title>Description</Title> <para> The &RHNPUSH; (<emphasis>rhnpush</emphasis>) pushes RPMs into locally managed channels on a &RHNSAT;. Refer to the Red Hat Satellite documentation for further reference. </para> </RefSect1> <RefSect1><Title>Options</Title> <variablelist> <varlistentry> <term>-v --verbose</term> <listitem> <para>Increase verbosity (can use multiple times).</para> </listitem> </varlistentry> <varlistentry> <term>-d<replaceable>DIRECTORY</replaceable> --dir=<replaceable>DIRECTORY</replaceable></term> <listitem> <para>process packages from this directory.</para> </listitem> </varlistentry> <varlistentry> <term>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable></term> <listitem> <para>process data for this specific channel (specified by label) only. NOTE: the channel's *label* is NOT the same as the channel's *name*.</para> </listitem> </varlistentry> <varlistentry> <term>-n<replaceable>N_HEADERS_PER_CALL</replaceable> --count=<replaceable>N_HEADERS_PER_CALL</replaceable></term> <listitem> <para>process this number of headers per call. Current default is 25. Must be an integer.</para> </listitem> </varlistentry> <varlistentry> <term>-l --list</term> <listitem> <para>only list the specified channels.</para> </listitem> </varlistentry> <varlistentry> <term>-r<replaceable>RELATIVE_DIRECTORY</replaceable> --reldir=<replaceable>RELATIVE_DIRECTORY</replaceable></term> <listitem> <para>relative directory to associate with each file.</para> </listitem> </varlistentry> <varlistentry> <term>-o<replaceable>ORGANIZATION_ID</replaceable> --orgid=<replaceable>ORGANIZATION_ID</replaceable></term> <listitem> <para>Your organization's ID number. Must be an integer.</para> </listitem> </varlistentry> <varlistentry> <term>-u<replaceable>USERNAME</replaceable> --username=<replaceable>USERNAME</replaceable></term> <listitem> <para>username of user that has administrative access to the specified channel.</para> </listitem> </varlistentry> <varlistentry> <term>-p<replaceable>PASSWORD</replaceable> --password=<replaceable>PASSWORD</replaceable></term> <listitem> <para>see --username.</para> </listitem> </varlistentry> <varlistentry> <term>-s --stdin</term> <listitem> <para>read the package names from standard-in (allows piping).</para> </listitem> </varlistentry> <varlistentry> <term>-X<replaceable>glob</replaceable> --exclude=<replaceable>GLOB</replaceable></term> <listitem> <para>exclude packages that matches this glob expression.</para> </listitem> </varlistentry> <varlistentry> <term>--force</term> <listitem> <para>force the package upload (overwrites if package is already uploaded).</para> </listitem> </varlistentry> <varlistentry> <term>--tolerant</term> <listitem> <para>If rhnpush errors while uploading a package, continue attempting to push the rest of the packages.</para> </listitem> </varlistentry> <varlistentry> <term>--nosig</term> <listitem> <para>don't fail if packages are unsigned.</para> </listitem> </varlistentry> <varlistentry> <term>--newest</term> <listitem> <para>push only the packages that are newer than those on the server.</para> <para>Please note that source RPMs are special in that their versions are never compared. Therefore, using this option with &RHNPUSH; will upload source RPMs for the specified channels only if they were not previously uploaded, and binaries built from them exist in those channels.</para> <para>In other words, you have to upload a binary rpm before being able to use <emphasis>--newest</emphasis> with its source rpm. </para> </listitem> </varlistentry> <varlistentry> <term>--timeout=<replaceable>SECONDS</replaceable></term> <listitem> <para>Change default connection timeout.</para> </listitem> </varlistentry> <varlistentry> <term>--nullorg</term> <listitem> <para>use the null org ID (most often the case).</para> </listitem> </varlistentry> <varlistentry> <term>--header</term> <listitem> <para>only upload the headers.</para> </listitem> </varlistentry> <varlistentry> <term>--source</term> <listitem> <para>the packages indicated are source packages (we treat them differently).</para> </listitem> </varlistentry> <varlistentry> <term>--server=<replaceable>SERVER</replaceable></term> <listitem> <para>push to this server. The format is http[s]://<hostname>/APP</para> </listitem> </varlistentry> <varlistentry> <term>--proxy=<replaceable>PROXY:PORT</replaceable></term> <listitem> <para>Use proxy server (<server>:<port>)</para> </listitem> </varlistentry> <varlistentry> <term>--test</term> <listitem> <para>only print the packages to be pushed, don't actually push them.</para> </listitem> </varlistentry> <varlistentry> <term>-N --new-cache</term> <listitem> <para>create a new username/password cache</para> </listitem> </varlistentry> <varlistentry> <term>--no-session-caching</term> <listitem> <para>This option disabled session token authentication. Useful if you want to push to two or more different servers.</para> </listitem> </varlistentry> <varlistentry> <term>--extended-test</term> <listitem> <para>perform a more verbose test.</para> </listitem> </varlistentry> <varlistentry> <term>-h --help</term> <listitem> <para>briefly describe the options.</para> </listitem> </varlistentry> <varlistentry> <term>-? --usage</term> <listitem> <para>usage summary.</para> </listitem> </varlistentry> </variablelist> </RefSect1> <RefSect1><Title>Notes on using rhnpush</Title> <simplelist> <member> Rhnpush has three configuration files called /etc/sysconfig/rhn/rhnpushrc, ~/.rhnpushrc, and ./.rhnpushrc. </member> <member> /etc/sysconfig/rhn/rhnpushrc is the system-wide default settings for rhnpush. </member> <member> ~/.rhnpushrc is the user-specific settings that override the system-wide settings. </member> <member> ./.rhnpushrc controls the directory specific settings that override the user-specific and system-specific settings. </member> <member> /etc/sysconfig/rhn/rhnpushrc must be present for rhnpush to function correctly. If it is missing, rhnpush will attempt to use a series of default settings stored internally as a replacement. ~/.rhnpushrc and ./.rhnpushrc are not required to be present, but will be used if they are present. They are not created automatically by rhnpush. </member> <member> Rhnpush uses a cache, stored at ~/.rhnpushcache, to temporarily hold the username and password for a user. </member> <member> If the cache is missing, it will be created by rhnpush. </member> <member> If the cache is present and not too old, the usename-password combo will be used as a convenience for the user. </member> <member> The amount of time a cache lasts is configurable in any of the three configuration files. </member> <member> If your username/password combination gets messed up you have two options. One, you can wait until the cache expires, which takes minutes by default. Two, you can use the --new_cache option to force rhnpush to let you reenter your username/password. </member> <member> Using the --stdin and --dir options at the same time works as follows: rhnpush will let you type in rpm names, each rpm name on a separate line. When you have finished entering in rpm names, hit Ctrl-D. Rhnpush will then grab the files from directory you specified with --dir, put them in a list with the rpms you listed through standard input, and send them to the channel that was listed on the command-line or in the configuration files. </member> </simplelist> </RefSect1> <RefSect1><Title>See Also</Title> <simplelist> <member>db-control(1) - embedded database environment only!</member> <member>rhn-charsets(8)</member> <member>rhn-schema-version(8)</member> <member>rhn-ssl-dbstore(8)</member> <member>satellite-debug(8)</member> <member>satellite-sync(8)</member> </simplelist> </RefSect1> <RefSect1><Title>Authors</Title> <simplelist> <member>Todd Warner <email>taw@redhat.com</email></member> <member>Mihai Ibanescu <email>misa@redhat.com</email></member> </simplelist> </RefSect1> </RefEntry> 07070100000010000081B400000000000000000000000161FA7D4F00000633000000000000000000000000000000000000001A00000000mgr-push/rhnpush_cache.py# # Copyright (c) 2008--2016 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # rhnpush_cache.py # # Classes that control the caching of usernames and passwords, # along with the retrieval of the username and password. # # UserInfo - Instantiations of this class are pickled. # Cache won't be valid after a certain amount of time. # # CacheManager - Controls access to the cache. import os from rhnpush import utils # This is the class that contains the session. class RHNPushSession: def __init__(self): self.location = os.path.join(utils.get_home_dir(), ".rhnpushcache") self.session = None def setSessionString(self, session): self.session = session def getSessionString(self): return self.session def readSession(self): sessionfile = open(self.location, "r") self.session = sessionfile.read() sessionfile.close() def writeSession(self): sessionfile = open(self.location, "w") sessionfile.write(self.session) sessionfile.close() 07070100000011000081B400000000000000000000000161FA7D4F0000149C000000000000000000000000000000000000001B00000000mgr-push/rhnpush_config.py# # Copyright (c) 2008--2017 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # The configuration file parser for the rhnpush utility. # The majority of this code is taken from rhncfg/config_common/local_config.py # # 11/11/2004 John Wregglesworth # import sys # pylint: disable=F0401 if sys.version_info[0] == 3: import configparser as ConfigParser else: import ConfigParser # Class that contains the options read in from the config file. # Uses a ConfigParser to create a dictionary of the configuration options. # That dictionary is then used to add instance variables to the object dynamically. class rhnpushConfigParser: # pylint: disable=W0201 _instance = None def __init__(self, filename=None, ensure_consistency=False): # Defaults that are used if the ensure_consistency parameter of the constructor is true # and the config file that is being read is missing some values. self.options_defaults = { 'newest': '0', 'usage': '0', 'header': '0', 'test': '0', 'nullorg': '0', 'source': '0', 'stdin': '0', 'verbose': '0', 'force': '0', 'nosig': '0', 'list': '0', 'exclude': '', 'files': '', 'orgid': '', 'reldir': '', 'count': '', 'dir': '', 'server': 'http://rhn.redhat.com/APP', 'channel': '', 'cache_lifetime': '600', 'new_cache': '0', 'extended_test': '0', 'no_session_caching': '0', 'proxy': '', 'tolerant': '0', 'ca_chain': '/usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT', 'timeout': None } # Used to parse the config file. self.settings = ConfigParser.ConfigParser() # use options from the rhnpush section. self.section = "rhnpush" self.username = None self.password = None if filename: self.filename = filename self._read_config_files() # Take all of the options read from the configuration file and add them as attributes #(instance variables, member variables, whatever) of this object. self._add_config_as_attr(ensure_consistency=ensure_consistency) # Use the ConfigParser to read in the configuration file. def _read_config_files(self): try: self.settings.read([self.filename]) except IOError: e = sys.exc_info()[1] print(("Config File Error: line %s, file %s: %s" % (e.lineno, e.filename, e))) sys.exit(1) def write(self, fileobj): try: self.settings.write(fileobj) except IOError: e = sys.exc_info()[1] print(("Config File Error: line %s, file %s: %s" % (e.lineno, e.filename, e))) sys.exit(1) # Returns an option read in from the configuration files and specified by the string variable option. # This function can probably be safely removed, since all configuration options become attributes # of an instantiation of this class. def get_option(self, option): try: return self.settings.get(self.section, option) except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): e = sys.exc_info()[1] print("Option/Section Error: line %s, file %s: %s" % (e.lineno, e.filename, e)) sys.exit(1) # Returns the keys of the attributes of the object. def keys(self): return list(self.__dict__.keys()) # Returns the keys of the options read in from the configuration files. def _keys(self): if self.settings.has_section(self.section): return self.settings.options(self.section) return () # Returns an option read in from the configuration files. def __getitem__(self, item): return self.get_option(item) def __delitem__(self, item): pass def __len__(self): pass def __setitem__(self, key, value): pass # Takes all of the configuration options read in by the ConfigParser and makes them attributes of the object. def _add_config_as_attr(self, ensure_consistency=False): for k in self._keys(): self.__dict__[k] = self.settings.get(self.section, k) # ensuring consistency only checks for missing configuration option. if ensure_consistency: for thiskey in self.options_defaults: if thiskey not in self.__dict__: self.__dict__[thiskey] = self.options_defaults[thiskey] 07070100000012000081B400000000000000000000000161FA7D4F00001367000000000000000000000000000000000000002000000000mgr-push/rhnpush_confmanager.py# # Copyright (c) 2008--2016 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # import sys import os from rhnpush import rhnpush_config from rhnpush import utils class ConfManager: def __init__(self, optionparser, store_true_list): sysdir = '/etc/sysconfig/rhn' homedir = utils.get_home_dir() default = 'rhnpushrc' regular = '.rhnpushrc' deffile = os.path.join(sysdir, default) regfile = os.path.join(homedir, regular) cwdfile = os.path.join(os.getcwd(), regular) self.cfgFileList = [deffile, regfile, cwdfile] self.defaultconfig = rhnpush_config.rhnpushConfigParser(ensure_consistency=True) # Get a reference to the object containing command-line options self.cmdconfig = optionparser self.store_true_list = store_true_list # Change the files options of the self.userconfig # Change the exclude options of the self.userconfig def _files_to_list(self): # Change the files options to lists. if ('files' in self.defaultconfig.__dict__ and not isinstance(self.defaultconfig.files, type([]))): self.defaultconfig.files = [x.strip() for x in self.defaultconfig.files.split(',')] # Change the exclude options to list. if ('exclude' in self.defaultconfig.__dict__ and not isinstance(self.defaultconfig.__dict__['exclude'], type([]))): self.defaultconfig.exclude = [x.strip() for x in self.defaultconfig.exclude.split(',')] def get_config(self): for f in self.cfgFileList: if os.access(f, os.F_OK): if not os.access(f, os.R_OK): print(("rhnpush does not have read permission on %s" % f)) sys.exit(1) config2 = rhnpush_config.rhnpushConfigParser(f) self.defaultconfig, config2 = utils.make_common_attr_equal(self.defaultconfig, config2) self._files_to_list() # Change the channel string into a list of strings. # pylint: disable=E1103 if not self.defaultconfig.channel: # if no channel then make it null array instead of # an empty string array from of size 1 [''] . self.defaultconfig.channel = [] else: self.defaultconfig.channel = [x.strip() for x in self.defaultconfig.channel.split(',')] # Get the command line arguments. These take precedence over the other settings argoptions, files = self.cmdconfig.parse_args() # Makes self.defaultconfig compatible with argoptions by changing all '0' value attributes to None. _zero_to_none(self.defaultconfig, self.store_true_list) # If verbose isn't set at the command-line, it automatically gets set to zero. If it's at zero, change it to # None so the settings in the config files take precedence. if argoptions.verbose == 0: argoptions.verbose = None # Orgid, count, cache_lifetime, and verbose all need to be integers, just like in argoptions. if self.defaultconfig.orgid: self.defaultconfig.orgid = int(self.defaultconfig.orgid) if self.defaultconfig.count: self.defaultconfig.count = int(self.defaultconfig.count) if self.defaultconfig.cache_lifetime: self.defaultconfig.cache_lifetime = int(self.defaultconfig.cache_lifetime) if self.defaultconfig.verbose: self.defaultconfig.verbose = int(self.defaultconfig.verbose) if self.defaultconfig.timeout: self.defaultconfig.timeout = int(self.defaultconfig.timeout) # Copy the settings in argoptions into self.defaultconfig. self.defaultconfig, argoptions = utils.make_common_attr_equal(self.defaultconfig, argoptions) # Make sure files is in the correct format. if self.defaultconfig.files != files: self.defaultconfig.files = files return self.defaultconfig # Changes every option in config that is also in store_true_list that is set to '0' to None def _zero_to_none(config, store_true_list): for opt in config.keys(): for cmd in store_true_list: if str(opt) == cmd and config.__dict__[opt] == '0': config.__dict__[opt] = None 07070100000013000081FD00000000000000000000000161FA7D4F0000689D000000000000000000000000000000000000001900000000mgr-push/rhnpush_main.py# # Copyright (c) 2008--2017 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # """ Management tool for the Spacewalk Proxy. This script performs various management operations on the Spacewalk Proxy: - Creates the local directory structure needed to store local packages - Uploads packages from a given directory to the RHN servers - Optionally, once the packages are uploaded, they can be linked to (one or more) channels, and copied in the local directories for these channels. - Lists the RHN server's vision on a certain channel - Checks if the local image of the channel (the local directory) is in sync with the server's image, and prints the missing packages (or the extra ones) """ import os import random import sys import time # pylint: disable=W0402 from optparse import Option, OptionParser # pylint: disable=F0401,E0611 from rhn.connections import idn_ascii_to_puny from rhn import rpclib from rhn.i18n import sstr from uyuni.common.rhn_pkg import InvalidPackageError, package_from_filename from uyuni.common.usix import raise_with_tb from rhnpush.utils import tupleify_urlparse from rhnpush import rhnpush_confmanager, uploadLib, rhnpush_v2 if sys.version_info[0] == 3: import urllib.parse as urlparse else: import urlparse # Global settings BUFFER_SIZE = 65536 HEADERS_PER_CALL = 10 DEBUG = 0 RPMTAG_NOSOURCE = 1051 def main(): # Initialize a command-line processing object with a table of options optionsTable = [ Option('-v', '--verbose', action='count', help='Increase verbosity', default=0), Option('-d', '--dir', action='store', help='Process packages from this directory'), Option('-c', '--channel', action='append', help='Manage this channel (specified by label)'), Option('-n', '--count', action='store', help='Process this number of headers per call', type='int'), Option('-l', '--list', action='store_true', help='Only list the specified channels'), Option('-r', '--reldir', action='store', help='Relative dir to associate with the file'), Option('-o', '--orgid', action='store', help='Org ID', type='int'), Option('-u', '--username', action='store', help='Use this username to connect to RHN/Satellite'), Option('-p', '--password', action='store', help='Use this password to connect to RHN/Satellite'), Option('-s', '--stdin', action='store_true', help='Read the package names from stdin'), Option('-X', '--exclude', action='append', help='Exclude packages that match this glob expression'), Option('--force', action='store_true', help='Force the package upload (overwrites if already uploaded)'), Option('--nosig', action='store_true', help='Push unsigned packages'), Option('--newest', action='store_true', help='Only push the packages that are newer than the server ones'), Option('--nullorg', action='store_true', help='Use the null org id'), Option('--header', action='store_true', help='Upload only the header(s)'), Option('--source', action='store_true', help='Upload source package information'), Option('--server', action='store', help='Push to this server (http[s]://<hostname>/APP)'), Option('--proxy', action='store', help='Use proxy server (<server>:<port>)'), Option('--test', action='store_true', help='Only print the packages to be pushed'), Option('-?', '--usage', action='store_true', help='Briefly describe the options'), Option('-N', '--new-cache', action='store_true', help='Create a new username/password cache'), Option('--extended-test', action='store_true', help='Perform a more verbose test'), Option('--no-session-caching', action='store_true', help='Disables session-token authentication.'), Option('--tolerant', action='store_true', help='If rhnpush errors while uploading a package, continue uploading the rest of the packages.'), Option('--ca-chain', action='store', help='alternative SSL CA Cert'), Option('--timeout', action='store', type='int', metavar='SECONDS', help='Change default connection timeout.') ] # Having to maintain a store_true list is ugly. I'm trying to get rid of this. true_list = ['usage', 'test', 'source', 'header', 'nullorg', 'newest', 'nosig', 'force', 'list', 'stdin', 'new_cache', 'extended_test', 'no_session_caching', 'tolerant'] # pylint: disable=E1101,E1103 optionParser = OptionParser(option_list=optionsTable, usage="%prog [OPTION] [<package>]") manager = rhnpush_confmanager.ConfManager(optionParser, true_list) options = manager.get_config() upload = UploadClass(options, files=options.files) if options.usage: optionParser.print_usage() sys.exit(0) if options.proxy: options.proxy = idn_ascii_to_puny(options.proxy) if options.list: if not options.channel: upload.die(1, "Must specify a channel for --list to work") upload.list() return if options.dir and not options.stdin: upload.directory() elif options.stdin and not options.dir: upload.readStdin() elif options.dir and options.stdin: upload.readStdin() upload.directory() if options.exclude: upload.filter_excludes() if options.newest: if not options.channel: upload.die(1, "Must specify a channel for --newest to work") upload.newest() if not upload.files: if upload.newest: print("No new files to upload; exiting") else: print("Nothing to do (try --help for more options)") sys.exit(0) if options.test: upload.test() return if options.extended_test: upload.extended_test() return if options.header: upload.uploadHeaders() return ret = upload.packages() if ret != 0: return 1 class UploadClass(uploadLib.UploadClass): # pylint: disable=E1101,W0201,W0632 def __init__(self, options, files=None): uploadLib.UploadClass.__init__(self, options, files) self.url_v2 = None def setURL(self): server = sstr(idn_ascii_to_puny(self.options.server)) if server is None: self.die(1, "Required parameter --server not supplied") scheme, netloc, path, params, query, fragment = tupleify_urlparse( urlparse.urlparse(server)) if not netloc: # No schema - trying to patch it up ourselves? server = "http://%s" % server scheme, netloc, path, params, query, fragment = tupleify_urlparse( urlparse.urlparse(server)) if not netloc: self.die(2, "Invalid URL %s" % server) if path == '': path = '/APP' if scheme.lower() not in ('http', 'https'): self.die(3, "Unknown URL scheme %s" % scheme) self.url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) self.url_v2 = urlparse.urlunparse((scheme, netloc, "/PACKAGE-PUSH", params, query, fragment)) def setOrg(self): if self.options.nullorg: if self.options.force: self.die(1, "ERROR: You cannot force a package to a nullorg channel.") else: # They push things to the None org id self.orgId = '' else: self.orgId = self.options.orgid or -1 def setForce(self): if self.options.force: self.force = 4 else: self.force = None def setRelativeDir(self): self.relativeDir = self.options.reldir def setChannels(self): self.channels = self.options.channel or [] # pylint: disable=W0702 def _test_force(self): test_force_str = "Setting force flag: %s" test_force = "Passed" try: self.setForce() except: test_force = "Failed" print(test_force_str % test_force) def _test_set_org(self): test_set_org_str = "Setting the org: %s" test_set_org = "Passed" try: self.setOrg() except: test_set_org = "Failed" print(test_set_org_str % test_set_org) def _test_set_url(self): test_set_url_str = "Setting the URL: %s" test_set_url = "Passed" try: self.setURL() except: test_set_url = "Failed" print(test_set_url_str % test_set_url) def _test_set_channels(self): test_set_channels_str = "Setting the channels: %s" test_set_channels = "Passed" try: self.setChannels() except: test_set_channels = "Failed" print(test_set_channels_str % test_set_channels) def _test_username_password(self): test_user_pass_str = "Setting the username and password: %s" test_user_pass = "Passed" try: self.setUsernamePassword() except: test_user_pass = "Failed" print(test_user_pass_str % test_user_pass) def _test_set_server(self): test_set_server_str = "Setting the server: %s" test_set_server = "Passed" try: self.setServer() except: test_set_server = "Failed" print(test_set_server_str % test_set_server) def _test_connect(self): auth_ret = uploadLib.call(self.server.packages.test_login, self.username, self.password) if auth_ret == 1: test_auth = "Passed" else: test_auth = "Failed" print("Testing connection and authentication: %s" % test_auth) def _test_access(self): access_ret = callable(self.server.packages.channelPackageSubscriptionBySession) if access_ret == 1: test_access = "Passed" else: test_access = "Failed" print("Testing access to upload functionality on server: %s" % test_access) # 12/22/05 wregglej 173287 Added a this funtion to test the new session authentication stuff. # It still needs work. def _test_authenticate(self): self.authenticate() def extended_test(self): self._test_force() self._test_set_org() self._test_set_url() self._test_set_channels() self._test_username_password() self._test_set_server() self._test_connect() self._test_access() print("The files that would have been pushed:") self.test() def packages(self): self.setForce() # set the org self.setOrg() # set the URL self.setURL() # set the channels self.setChannels() # set the server self.setServer() # 12/22/05 wregglej 173287 authenticate the session. self.authenticate() # Do we have the new-style handler available? # ping the server for status self.warn(2, "url is", self.url_v2) ping = rhnpush_v2.PingPackageUpload(self.url_v2, self.options.proxy) ping_status, errmsg, headerinfo = ping.ping() self.warn(2, "Result codes:", ping_status, errmsg) # move patch clusters to the end because all the patches in the cluster # have to be pushed before the cluster itself files1 = [] files2 = [] for filename in self.files: if filename.startswith('patch-cluster-'): files2.append(filename) else: files1.append(filename) self.files = files1 + files2 channel_packages = [] # a little fault tolarence is in order random.seed() tries = 3 # satellites < 4.1.0 are no more supported if sys.version_info[0] == 3: pack_exist_check = headerinfo.get('X-RHN-Check-Package-Exists') else: pack_exist_check = headerinfo.getheader('X-RHN-Check-Package-Exists') if not pack_exist_check: self.die(-1, "Pushing to Satellite < 4.1.0 is not supported.") (server_digest_hash, pkgs_info, digest_hash) = self.check_package_exists() for pkg in self.files: ret = None # pkilambi:errors off as not initialized.this fixes it. # temporary fix for picking pkgs instead of full paths pkg_key = (pkg.strip()).split('/')[-1] if pkg_key not in server_digest_hash: continue checksum_type, checksum = digest = digest_hash[pkg_key] server_digest = tuple(server_digest_hash[pkg_key]) # compare checksums for existance check if server_digest == digest and not self.options.force: channel_packages.append(pkgs_info[pkg_key]) self.warn(1, "Package %s already exists on the SUSE Manager Server-- Skipping Upload...." % pkg) continue elif server_digest == (): self.warn(1,"Package %s Not Found on SUSE Manager Server -- Uploading" % pkg) elif server_digest == "on-disk" and not self.options.force: channel_packages.append(pkgs_info[pkg_key]) self.warn(0, "Package on disk but not on db -- Skipping Upload " % pkg) continue elif server_digest != digest: if self.options.force: self.warn(1, "Package checksum %s mismatch -- Forcing Upload" % pkg) else: msg = "Error: Package %s already exists on the server with" \ " a different checksum. Skipping upload to prevent" \ " overwriting existing package. (You may use rhnpush with" \ " the --force option to force this upload if the" \ " force_upload option is enabled on your server.)\n" % pkg if not self.options.tolerant: self.die(-1, msg) self.warn(0, msg) continue for _t in range(0, tries): try: ret = self.package(pkg, checksum_type, checksum) if ret is None: raise uploadLib.UploadError() # TODO: Revisit this. We throw this error all over the place, # but doing so will cause us to skip the --tolerant logic # below. I don't think we really want this behavior. # There are some cases where we don't want to retry 3 # times, but not at the expense of disabling the tolerant # flag, IMHO. This loop needs some lovin'. -- pav # FIX: it checks for tolerant flag and aborts only if the flag is #not specified except uploadLib.UploadError: ue = sys.exc_info()[1] if not self.options.tolerant: self.die(1, ue) self.warn(2, ue) except AuthenticationRequired: # session expired so we re-authenticate for the process to complete # this uses the username and password from memory if available # else it prompts for one. self.authenticate() except: self.warn(2, sys.exc_info()[1]) wait = random.randint(1, 5) self.warn(0, "Waiting %d seconds and trying again..." % wait) time.sleep(wait) # The else clause gets executed in the stuff in the try-except block *succeeds*. else: break # if the preceeding for-loop exits without a call to break, then this else clause gets called. # What's kind of weird is that if the preceeding for-loop doesn't call break then an error occurred # and all of retry attempts failed. If the for-loop *does* call break then everything is hunky-dory. # In short, this else clause only get's called if something is F.U.B.A.R and the retry attempts don't # fix anything. else: if not self.options.tolerant: # pkilambi:bug#176358:this exits with a error code of 1 self.die(1, "Giving up after %d attempts" % tries) else: print("Giving up after %d attempts and continuing on..." % (tries,)) # 5/13/05 wregglej - 154248 ?? we still want to add the packages if they're source. if ret and self.channels: # and ret['arch'] != 'src': # Don't bother to add the package if # no channel was specified or a source rpm was passed channel_packages.append(ret) # self.channels is never None, it always has at least one entry with an empty string. if len(self.channels) == 1 and self.channels[0] == '': return info = { 'packages': channel_packages, 'channels': self.channels } if self.orgId == '' or self.orgId > 0: info['orgId'] = self.orgId # 2/3/06 wregglej 173287 Added check to see if we can use session tokens. if channel_packages: self.authenticate() uploadLib.call(self.server.packages.channelPackageSubscriptionBySession, self.session.getSessionString(), info) return 0 # does an existance check of the packages to be uploaded and returns their checksum and other info def check_package_exists(self): self.warn(2, "Computing checksum and package info. This may take some time ...") pkg_hash = {} digest_hash = {} for pkg in self.files: pkg_info = {} pkg_key = (pkg.strip()).split('/')[-1] if not os.access(pkg, os.R_OK): if not self.options.tolerant: self.die(-1, "Could not read file %s" % pkg) self.warn(-1, "Could not read file %s" % pkg) continue try: a_pkg = package_from_filename(pkg) a_pkg.read_header() a_pkg.payload_checksum() except InvalidPackageError: if not self.options.tolerant: self.die(-1, "ERROR: %s: This file doesn't appear to be a package" % pkg) self.warn(2, "ERROR: %s: This file doesn't appear to be a package" % pkg) continue except IOError: if not self.options.tolerant: self.die(-1, "ERROR: %s: No such file or directory available" % pkg) self.warn(2, "ERROR: %s: No such file or directory available" % pkg) continue digest_hash[pkg_key] = (a_pkg.checksum_type, a_pkg.checksum) a_pkg.input_stream.close() for tag in ('name', 'version', 'release', 'epoch', 'arch'): val = a_pkg.header[tag] if val is None: val = '' pkg_info[tag] = val # b195903:the arch for srpms should be obtained by is_source check # instead of checking arch in header if a_pkg.header.is_source: if not self.options.source: self.die(-1, "ERROR: Trying to Push src rpm, Please re-try with --source.") if RPMTAG_NOSOURCE in a_pkg.header.keys(): pkg_info['arch'] = 'nosrc' else: pkg_info['arch'] = 'src' pkg_info['checksum_type'] = a_pkg.checksum_type pkg_info['checksum'] = a_pkg.checksum pkg_hash[pkg_key] = pkg_info if self.options.nullorg: # to satisfy xmlrpc from None values. orgid = 'null' else: orgid = '' info = { 'packages': pkg_hash, 'channels': self.channels, 'org_id': orgid, 'force': self.options.force or 0 } # rpc call to get checksum info for all the packages to be uploaded if not self.options.source: # computing checksum and other info is expensive process and session # could have expired.Make sure its re-authenticated. self.authenticate() if uploadLib.exists_getPackageChecksumBySession(self.server): checksum_data = uploadLib.getPackageChecksumBySession(self.server, self.session.getSessionString(), info) else: # old server only md5 capable checksum_data = uploadLib.getPackageMD5sumBySession(self.server, self.session.getSessionString(), info) else: # computing checksum and other info is expensive process and session # could have expired.Make sure its re-authenticated. self.authenticate() if uploadLib.exists_getPackageChecksumBySession(self.server): checksum_data = uploadLib.getSourcePackageChecksumBySession(self.server, self.session.getSessionString(), info) else: # old server only md5 capable checksum_data = uploadLib.getSourcePackageMD5sumBySession(self.server, self.session.getSessionString(), info) return (checksum_data, pkg_hash, digest_hash) def package(self, package, fileChecksumType, fileChecksum): self.warn(1, "Uploading package %s" % package) if not os.access(package, os.R_OK): self.die(-1, "Could not read file %s" % package) try: h = uploadLib.get_header(package, source=self.options.source) except uploadLib.UploadError: e = sys.exc_info()[1] # GS: MALFORMED PACKAGE print("Unable to load package", package, ":", e) return None if hasattr(h, 'packaging'): packaging = h.packaging else: packaging = 'rpm' if packaging == 'rpm' and self.options.nosig is None and not h.is_signed(): # pkilambi:bug#173886:force exit to check for sig if --nosig raise uploadLib.UploadError("ERROR: %s: unsigned rpm (use --nosig to force)" % package) try: ret = self._push_package_v2(package, fileChecksumType, fileChecksum) except uploadLib.UploadError: e = sys.exc_info()[1] ret, diff_level, pdict = e.args[:3] severities = { 1: 'path changed', 2: 'package resigned', 3: 'differing build times or hosts', 4: 'package recompiled', } if diff_level in severities: strmsg = \ "Error: Package with same name already exists on " + \ "server but contents differ (" + \ severities[diff_level] + \ "). Use --force or remove old package before " + \ "uploading the newer version." else: strmsg = "Error: severity %s" % diff_level self.warn(-1, "Uploading failed for %s\n%s\n\tDiff: %s" % (package, strmsg, pdict['diff']['diff'])) if diff_level != 1: # This will prevent us from annoyingly retrying when there is # no reason to. raise uploadLib.UploadError() return ret return ret def _push_package_v2(self, package, fileChecksumType, fileChecksum): self.warn(1, "Using POST request") pu = rhnpush_v2.PackageUpload(self.url_v2, self.options.proxy) pu.set_session(self.session.getSessionString()) pu.set_force(self.options.force) pu.set_null_org(self.options.nullorg) pu.set_timeout(self.options.timeout) status, msgstr = pu.upload(package, fileChecksumType, fileChecksum) ret = {} for tag in ('name', 'version', 'release', 'epoch', 'arch'): val = getattr(pu, "package_%s" % tag) if val is None: val = '' ret[tag] = val ret['checksum_type'] = fileChecksumType ret['checksum'] = fileChecksum if status == 400: # Bad request - something bad happened try: data = rpclib.xmlrpclib.loads(msgstr) except: # Raise the exception instead of silently dying raise_with_tb(uploadLib.UploadError("Error pushing %s: %s (%s)" % (package, msgstr, status)), sys.exc_info()[2]) (diff_dict, ), methodname = data del methodname diff_level = diff_dict['level'] pdict = diff_dict['diff'] raise uploadLib.UploadError(ret, diff_level, pdict) if status == 403: # auth expired raise an exception to grab one raise AuthenticationRequired() if status != 200: self.die(1, "Error pushing %s: %s (%s)" % (package, msgstr, status)) return ret class AuthenticationRequired(Exception): pass if __name__ == '__main__': # test code sys.exit(main() or 0) 07070100000014000081B400000000000000000000000161FA7D4F00000912000000000000000000000000000000000000001700000000mgr-push/rhnpush_v2.py# # Copyright (c) 2008--2016 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # Package uploading tool # import base64 from rhnpush import connection class PackageUpload(connection.PackageUpload): user_agent = "rhnpush" def set_auth(self, username, password): auth_vals = self.encode_values([username, password]) self.headers["%s-%s" % (self.header_prefix, "Auth")] = auth_vals def set_session(self, session_string): self.headers["%s-%s" % (self.header_prefix, "Auth-Session")] = session_string def set_force(self, force): if force: force = 1 else: force = 0 self.headers["%s-%s" % (self.header_prefix, "Force")] = str(force) def set_null_org(self, null_org): if null_org: self.headers["%s-%s" % (self.header_prefix, "Null-Org")] = "1" def set_timeout(self, timeout): self.connection.set_timeout(timeout) # Encodes an array of variables into Base64 (column-separated) @staticmethod def encode_values(arr): val = ':'.join([x.strip() for x in map(base64.encodestring, arr)]) # Get rid of the newlines val = val.replace('\n', '') # And split the result into lines of fixed size line_len = 80 result = [] start = 0 while 1: if start >= len(val): break result.append(val[start:start + line_len]) start = start + line_len return result class PingPackageUpload(connection.PackageUpload): user_agent = "rhnpush-ping" def ping(self): self.send_http("GET") # return the header info as well to check for capabilities. return self._response.status, self._response.reason, self._response.msg 07070100000015000081B400000000000000000000000161FA7D4F0000060D000000000000000000000000000000000000001300000000mgr-push/rhnpushrc#This is the default config file for rhnpush. Place it in your home directory. [rhnpush] #Only push the packages that are newer than those on the server newest = 0 #Briefly describe the options usage = 0 #Upload only the header(s) header = 0 #Only print the packages that would otherwise have been pushed test = 0 #Use the null org id nullorg = 0 #Upload source package information source = 0 #Read the package names from stdin stdin = 0 #Increase verbosity verbose = 0 #Force the package upload (overwrites if already uploaded) force = 0 #Push unsigned packages nosig = 0 #Only list the specified channels list = 0 #Exclude the packages that match this glob expression exclude = files = #Organization ID orgid = #Relative directory to associate with the package reldir = #Process this number of headers per call count = #Process packages from this directory dir = #Push to this server (http[s]://<hostname>/APP) server = https://localhost/APP #Manage this channel(s) channel = #Determines how long the cache will last, in seconds cache_lifetime = 600 #Reset the cache new_cache = 0 #Run the extended version of the test extended_test = 0 #Disable the session-token support no_session_caching = 0 #Have rhnpush be tolerant of errors when pushing a large number of packages. tolerant = 0 #The CA cert used to verify the ssl server ca_chain = /usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT #Default connection timeout, (no value for default) timeout = 300 07070100000016000081B400000000000000000000000161FA7D4F000015AF000000000000000000000000000000000000001400000000mgr-push/rpm2mpm.py# # Copyright (c) 2008--2016 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # import sys import time # pylint: disable=F0401 from uyuni.common import rhn_rpm, rhn_mpm from uyuni.common.usix import ListType, TupleType def main(): packages = sys.argv[1:] if not packages: return for pkgfile in packages: # Try to open the package as a patch first with open(pkgfile) as f: header = rhn_rpm.get_package_header(file_obj=f) p = rpm_to_mpm(header, f) dest_filename = _compute_filename(p.header) print("Writing out the package to %s" % dest_filename) with open(dest_filename, "w+") as dest_file: p.write(dest_file) def _compute_filename(hdr): return '%s-%s.%s.mpm' % (hdr['name'], hdr['version'], hdr['arch']) def rpm_to_mpm(header, file_stream): tag_map = { 'package_group': 'group', 'rpm_version': 'rpmversion', 'payload_size': 'archivesize', 'payload_format': 'payloadformat', 'build_host': 'buildhost', 'build_time': 'buildtime', 'source_rpm': 'sourcerpm', } tags = [ 'name', 'epoch', 'version', 'release', 'arch', 'description', 'summary', 'license', 'package_group', 'rpm_version', 'payload_size', 'payload_format', 'build_host', 'build_time', 'cookie', 'vendor', 'source_rpm', 'sigmd5', 'sigpgp', 'siggpg', 'sigsize', ] result = {} for t in tags: tt = tag_map.get(t, t) result[t] = header[tt] # Add files result['files'] = _extract_files(header) # Dependency result['provides'] = _extract_rpm_requires(header) result['requires'] = _extract_rpm_provides(header) result['conflicts'] = _extract_rpm_conflicts(header) result['obsoletes'] = _extract_rpm_obsoletes(header) result['changelog'] = _extract_rpm_changelog(header) # md5sum, package_size file_stream.seek(0, 2) file_size = file_stream.tell() result['package_size'] = file_size is_source = 0 if header.is_source: is_source = 1 result['is_source'] = is_source result['package_type'] = 'rpm' h = rhn_mpm.MPM_Header(result) p = rhn_mpm.MPM_Package() p.header = h p.payload_stream = file_stream return p def _extract_files(header): tag_maps = { 'name': 'filenames', 'device': 'filedevices', 'inode': 'fileinodes', 'file_mode': 'filemodes', 'username': 'fileusername', 'groupname': 'filegroupname', 'rdev': 'filerdevs', 'file_size': 'filesizes', 'mtime': 'filemtimes', 'md5': 'filemd5s', 'linkto': 'filelinktos', 'flags': 'fileflags', 'verifyflags': 'fileverifyflags', 'lang': 'filelangs', } files = _extract_array_fields(header, tag_maps) # Munge the mtime for f in files: f['mtime'] = gmtime(f['mtime']) return files def _extract_rpm_provides(header): tag_maps = { 'name': 'provides', 'version': 'provideversion', 'flags': 'provideflags', } return _extract_array_fields(header, tag_maps) def _extract_rpm_requires(header): tag_maps = { 'name': 'requirename', 'version': 'requireversion', 'flags': 'requireflags', } return _extract_array_fields(header, tag_maps) def _extract_rpm_conflicts(header): tag_maps = { 'name': 'conflictname', 'version': 'conflictversion', 'flags': 'conflictflags', } return _extract_array_fields(header, tag_maps) def _extract_rpm_obsoletes(header): tag_maps = { 'name': 'obsoletename', 'version': 'obsoleteversion', 'flags': 'obsoleteflags', } return _extract_array_fields(header, tag_maps) def _extract_rpm_changelog(header): tag_maps = { 'name': 'changelogname', 'text': 'changelogtext', 'time': 'changelogtime', } cl = _extract_array_fields(header, tag_maps) # Munge the changelog time for c in cl: c['time'] = gmtime(c['time']) return cl def _extract_array_fields(header, tag_maps): # First determine the number of entries key = list(tag_maps.keys())[0] rpmtag = tag_maps.get(key) arr = header[rpmtag] if arr is None: # nothing to do return [] count = len(arr) result = [] for i in range(count): tag_dict = {} for key, rpmtag in tag_maps.items(): arr = header[rpmtag] if not isinstance(arr, (ListType, TupleType)): arr = [arr] tag_dict[key] = arr[i] result.append(tag_dict) return result def gmtime(timestamp): ttuple = time.gmtime(timestamp) return "%d-%02d-%02d %02d:%02d:%02d" % ttuple[:6] if __name__ == '__main__': sys.exit(main() or 0) 07070100000017000041FD00000000000000000000000161FA7D4F00000000000000000000000000000000000000000000000E00000000mgr-push/test07070100000018000081B400000000000000000000000161FA7D4F000010E7000000000000000000000000000000000000002200000000mgr-push/test/testRhnpushCache.py# # Copyright (c) 2008--2015 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # import unittest import rhnpush_cache import time class UserInfoTestCase(unittest.TestCase): def setUp(self): self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password') def tearDown(self): self.userinfo = None def testCheckCacheTrue(self): assert self.userinfo.checkCache() == True def testCheckCacheFalse(self): time.sleep(7) assert self.userinfo.checkCache() == False def testSetUsernamePassword(self): self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password') self.userinfo.setUsernamePassword('aaaa', 'bbbb') assert self.userinfo.username != 'wregglej' and self.userinfo.password != 'password' def testSetUsernamePassword3(self): self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password') self.userinfo.setUsernamePassword('aaaa', 'bbbb') assert self.userinfo.username == 'aaaa' and self.userinfo.password == 'bbbb' def testGetUsernamePassword(self): self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password') assert self.userinfo.username == 'wregglej' and self.userinfo.password == 'password' def testIsFresh(self): self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password') assert self.userinfo.isFresh() == True def testIsntFresh(self): time.sleep(6) assert self.userinfo.isFresh() == False def testSetCacheLifetime(self): self.userinfo = rhnpush_cache.UserInfo(5, username='wregglej', password='password') self.userinfo.setCacheLifetime(6667) assert self.userinfo.cache_lifetime != 5 and self.userinfo.cache_lifetime == 6667 def testGetTimeLeft(self): self.userinfo = rhnpush_cache.UserInfo(10, username='wregglej', password='password') time.sleep(2.0) assert self.userinfo.getTimeLeft() >= 7.98 and self.userinfo.getTimeLeft() <= 8.002 class CacheManagerTestCase(unittest.TestCase): def setUp(self): self.cache = rhnpush_cache.CacheManager(5) def tearDown(self): self.cache = None def testIsFresh(self): self.cache = rhnpush_cache.CacheManager(5) self.cache.setUsernamePassword('a', 'b') assert self.cache.isFresh() == True def testIsntFresh(self): self.cache = rhnpush_cache.CacheManager(5) time.sleep(7) assert self.cache.isFresh() == False def testSetUsernamePassword(self): self.cache = rhnpush_cache.CacheManager(5) self.cache.setUsernamePassword('wregglej', 'password') # print self.cache.cache.username assert self.cache.cache.username == 'wregglej' and self.cache.cache.password == 'password' def testSetUsernamePassword2(self): self.cache = rhnpush_cache.CacheManager(5) self.cache.setUsernamePassword('wregglej', 'password') self.cache.setUsernamePassword('aaaa', 'bbbb') assert self.cache.cache.username == 'aaaa' and self.cache.cache.password == 'bbbb' def testGetUsernamePassword(self): self.cache = rhnpush_cache.CacheManager(5) self.cache.setUsernamePassword('wregglej', 'password') u, p = self.cache.getUsernamePassword() assert u == 'wregglej' and p == 'password' def testSetCacheLifetime(self): self.cache = rhnpush_cache.CacheManager(5) self.cache.setCacheLifetime(10) assert self.cache.cache.cache_lifetime == 10 and self.cache.cache.cache_lifetime != 5 def testWriteCache(self): pass def testGetTimeLeft(self): pass if __name__ == "__main__": unittest.main() 07070100000019000081B400000000000000000000000161FA7D4F000007E0000000000000000000000000000000000000002300000000mgr-push/test/testRhnpushConfig.py# # Copyright (c) 2008--2016 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # import rhnpush_config import unittest # pylint: disable=W0212,E1101,R0904 class RhnConfigTestCase(unittest.TestCase): def setUp(self): self.userconfig = rhnpush_config.rhnpushConfigParser('.rhnpushrc') self.defaultconfig = rhnpush_config.rhnpushConfigParser('/etc/sysconfig/rhn/rhnpushrc') def tearDown(self): self.userconfig = None self.defaultconfig = None def testReadConfigFiles(self): self.userconfig._read_config_files() self.defaultconfig._read_config_files() assert self.userconfig.settings != None and self.defaultconfig.settings != None def testGetOption(self): a = self.userconfig.get_option('usage') b = self.defaultconfig.get_option('usage') assert a != None and b != None and a == '0' and b == '0' def testKeys(self): a = list(self.userconfig.keys()) b = list(self.defaultconfig.keys()) assert a != None and b != None def test_keys(self): a = self.userconfig._keys() b = self.defaultconfig._keys() assert a != None and b != None def testGetItem(self): pass def testAddConfigAsAttr(self): self.userconfig._add_config_as_attr() self.userconfig._add_config_as_attr() assert self.userconfig.usage != None and self.defaultconfig.usage != None if __name__ == "__main__": unittest.main() 0707010000001A000081B400000000000000000000000161FA7D4F000007C9000000000000000000000000000000000000001B00000000mgr-push/test/testUtils.py# # Copyright (c) 2008--2016 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # import unittest import utils class TestObj1: def __init__(self): self.a = 1 self.b = 2 self.c = 3 self._d = '1' self._e = '2' @staticmethod def _private_function(): print("This is privatei to TestObj1 instances") @staticmethod def public_function(): print("This is public and belongs to TestObj1") class TestObj2: def __init__(self): self.a = 4 self.b = 5 self.c = 6 self._d = '4' self._e = '5' self.f = 'aaa' @staticmethod def _private_function(): print("This is private to TestObj2 instances") @staticmethod def public_function(): print("This is public and belongs to TestObj2") class UtilsTestCase(unittest.TestCase): def setUp(self): self.obj1 = TestObj1() self.obj2 = TestObj2() def tearDown(self): self.obj1 = None self.obj2 = None # pylint: disable=W0212 def testMakeCommonAttrEqual(self): self.obj1, self.obj2 = utils.make_common_attr_equal(self.obj1, self.obj2) assert (self.obj1._d == '1' and self.obj2._d == '4' and self.obj1.a == 4 and self.obj1.b == 5 and self.obj1.c == 6 and self.obj2.f == 'aaa') if __name__ == "__main__": unittest.main() 0707010000001B000081B400000000000000000000000161FA7D4F00000A9F000000000000000000000000000000000000001E00000000mgr-push/test/test_archive.py# # Copyright (c) 2008--2015 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # import os import unittest import zipfile # test import import archive # globals ---------------------------------------------------------------- TEST_ARCHIVE = "/tmp/test_archive.zip" TEST_DIR = "./test_archive/foo/bar" TEST_FILE = "test_file" TEST_FILE_PATH = os.path.join(TEST_DIR, TEST_FILE) TEST_CONTENTS = """ ONE: foo TWO: you THREE: too FOUR: foo """ # test case -------------------------------------------------------------- class ArchiveTest(unittest.TestCase): def setUp(self): if not os.path.isdir(TEST_DIR): os.makedirs(TEST_DIR) fd = open(TEST_FILE_PATH, 'w') fd.write(TEST_CONTENTS) fd.close() fzip = zipfile.ZipFile(TEST_ARCHIVE, 'w') fzip.write(TEST_FILE_PATH) fzip.close() def tearDown(self): if os.path.isfile(TEST_FILE_PATH): os.unlink(TEST_FILE_PATH) if os.path.isdir(TEST_DIR): os.removedirs(TEST_DIR) if os.path.isfile(TEST_ARCHIVE): os.unlink(TEST_ARCHIVE) # test methods ------------------------------------------------------- @staticmethod def testInstantiation(): "test the instantiation of an archive parser object" p = archive.get_archive_parser(TEST_ARCHIVE) assert isinstance(p, archive.ArchiveParser) @staticmethod def testFind(): "test the ability of the parser to find a file in the archive" p = archive.get_archive_parser(TEST_ARCHIVE) assert p.contains(TEST_FILE) @staticmethod def testFindPath(): "test the ability of the parser to find a subpath in the archive" p = archive.get_archive_parser(TEST_ARCHIVE) assert p.contains("foo/bar/" + TEST_FILE) @staticmethod def testRead(): "test the ability of the parser to read a file in the archive" p = archive.get_archive_parser(TEST_ARCHIVE) contents = p.read(TEST_FILE) assert contents == TEST_CONTENTS # run the tests ---------------------------------------------------------- if __name__ == "__main__": unittest.main() 0707010000001C000081B400000000000000000000000161FA7D4F0000687F000000000000000000000000000000000000001600000000mgr-push/uploadLib.py# # Copyright (c) 2008--2017 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # system imports import os import sys import fnmatch import getpass # imports # pylint: disable=F0401,E0611 # exceptions # pylint: disable=W0702,W0703 import inspect from uyuni.common import rhn_mpm from uyuni.common.rhn_pkg import package_from_filename, get_package_header from uyuni.common.usix import raise_with_tb from up2date_client import rhnserver from rhn.i18n import sstr from rhnpush import rhnpush_cache if sys.version_info[0] == 3: import xmlrpc.client as xmlrpclib else: import xmlrpclib try: from rhn import rpclib # pylint: disable=C0412 Binary = rpclib.xmlrpclib.Binary Output = rpclib.transports.Output except ImportError: # old-style xmlrpclib library rpclib = xmlrpclib Binary = rpclib.Binary # pylint: disable=F0401 import cgiwrap Output = cgiwrap.Output # Buffer size we use for copying BUFFER_SIZE = 65536 HEADERS_PER_CALL = 25 # Exception class class UploadError(Exception): pass class ServerFault(Exception): def __init__(self, faultCode=None, faultString="", faultExplanation=""): Exception.__init__(self) self.faultCode = faultCode self.faultString = faultString self.faultExplanation = faultExplanation class UploadClass: """Functionality for an uploading tool """ def __init__(self, options, files=None): self.options = options self.username = None self.password = None self.proxy = None self.proxyUsername = None self.proxyPassword = None self.ca_chain = None self.force = None self.files = files or [] self.new_sat = None self.url = None self.channels = None self.count = None self.server = None self.session = None self.orgId = None self.relativeDir = None self.use_session = True self.use_checksum_paths = False def warn(self, verbose, *args): if self.options.verbose >= verbose: ReportError(*args) @staticmethod def die(errcode, *args): ReportError(*args) # pkilambi:bug#176358:this should exit with error code sys.exit(errcode) def setURL(self): # Redefine this in derived classes self.url = None def setUsernamePassword(self): # Use the stored values, if available username = self.username or self.options.username password = self.password or self.options.password self.username, self.password = getUsernamePassword(username, password) def setProxyUsernamePassword(self): self.proxyUsername = None self.proxyPassword = None def setCAchain(self): self.ca_chain = self.options.ca_chain def setProxy(self): if self.options.proxy is None or self.options.proxy == '': self.proxy = None else: self.proxy = "http://%s" % self.options.proxy def setForce(self): self.force = None def setServer(self): # set the proxy self.setProxy() if self.proxy is None: self.warn(1, "Connecting to %s" % self.url) else: self.warn(1, "Connecting to %s (via proxy '%s')" % (self.url, self.proxy)) # set the CA chain self.setCAchain() # set the proxy username and password self.setProxyUsernamePassword() self.server = getServer(self.url, self.proxy, self.proxyUsername, self.proxyPassword, self.ca_chain) # Compress the output, just to be fast self.server.set_transport_flags( transfer=Output.TRANSFER_BINARY, encoding=Output.ENCODE_GZIP) def setChannels(self): if not self.options.channel: self.die(-1, "No channel was specified") self.channels = self.options.channel self.warn(1, "Channels: %s" % ' '.join(self.channels)) setNoChannels = setChannels def setOrg(self): self.orgId = -1 def setCount(self): if not self.options.count: self.count = HEADERS_PER_CALL else: self.count = self.options.count def setRelativeDir(self): self.relativeDir = None def directory(self): self.warn(2, "Uploading files from directory", self.options.dir) for filename in listdir(self.options.dir): # only add packages if filename[-3:] in ("rpm", "mpm"): self.files.append(filename) def filter_excludes(self): if not self.options.exclude: return self for f in self.files[:]: bf = os.path.basename(f) for pattern in self.options.exclude: if fnmatch.fnmatch(bf, pattern): self.warn(1, "Ignoring %s" % f) self.files.remove(f) return self def readStdin(self): self.warn(1, "Reading package names from stdin") self.files = self.files + readStdin() def _listChannelSource(self): if self.use_session: return listChannelSourceBySession(self.server, self.session.getSessionString(), self.channels) return listChannelSource(self.server, self.username, self.password, self.channels) def _listChannel(self): if self.use_session: if self.use_checksum_paths: return listChannelChecksumBySession(self.server, self.session.getSessionString(), self.channels) return listChannelBySession(self.server, self.session.getSessionString(), self.channels) if self.use_checksum_paths: return listChannelChecksum(self.server, self.username, self.password, self.channels) return listChannel(self.server, self.username, self.password, self.channels) def list(self): # set the URL self.setURL() # set the channels self.setChannels() # set the server self.setServer() self.authenticate() if self.options.source: channel_list = self._listChannelSource() else: # List the channel's contents channel_list = self._listChannel() for p in channel_list: print(p[:6]) def newest(self): # set the URL self.setURL() # set the channels self.setChannels() # set the server self.setServer() self.authenticate() sources = self.options.source if sources: return self.get_missing_source_packages() return self.get_newest_binary_packages() def get_newest_binary_packages(self): # Loop through the args and only keep the newest ones localPackagesHash = {} for filename in self.files: nvrea = self._processFile(filename, nosig=1)['nvrea'] name = nvrea[0] if name not in localPackagesHash: localPackagesHash[name] = {nvrea: filename} continue same_names_hash = localPackagesHash[name] # Already saw this name if nvrea in same_names_hash: # Already seen this nvrea continue skip_rpm = 0 for local_nvrea in same_names_hash.keys(): # XXX is_mpm should be set accordingly ret = packageCompare(local_nvrea, nvrea, is_mpm=0) if ret == 0 and local_nvrea[4] == nvrea[4]: # Weird case, we've already compared the two skip_rpm = 1 break if ret > 0: # nvrea is older than local_nvrea skip_rpm = 1 break if ret < 0: # nvrea is newer than local_nvrea del same_names_hash[local_nvrea] # Different arches - go on if skip_rpm: # Older continue same_names_hash[nvrea] = filename # Now get the list from the server pkglist = self._listChannel() for p in pkglist: name = p[0] if name not in localPackagesHash: # Not in the local list continue same_names_hash = localPackagesHash[name] remote_nvrea = tuple(p[:5]) if remote_nvrea in same_names_hash: # The same package is already uploaded del same_names_hash[remote_nvrea] continue for local_nvrea in list(same_names_hash.keys()): # XXX is_mpm sould be set accordingly ret = packageCompare(local_nvrea, remote_nvrea, is_mpm=0) if ret < 0: # The remote package is newer than the local one del same_names_hash[local_nvrea] continue if ret == 0 and local_nvrea[4] == remote_nvrea[4]: # Same arch del same_names_hash[local_nvrea] continue # This means local is newer # Return the list of files to push l = [] for fhash in localPackagesHash.values(): for filename in fhash.values(): l.append(filename) l.sort() self.files = l def _listMissingSourcePackages(self): if self.use_session: return listMissingSourcePackagesBySession(self.server, self.session.getSessionString(), self.channels) return listMissingSourcePackages(self.server, self.username, self.password, self.channels) def get_missing_source_packages(self): localPackagesHash = {} for filename in self.files: localPackagesHash[os.path.basename(filename)] = filename # Now get the list from the server pkglist = self._listMissingSourcePackages() to_push = [] for pkg in pkglist: pkg_name, _pkg_channel = pkg[:2] if pkg_name not in localPackagesHash: # We don't have it continue to_push.append(localPackagesHash[pkg_name]) to_push.sort() self.files = to_push return self.files def test(self): # Test only for p in self.files: print(p) def _get_files(self): return self.files[:] def _uploadSourcePackageInfo(self, info): if self.use_session: return call(self.server.packages.uploadSourcePackageInfoBySession, self.session.getSessionString(), info) return call(self.server.packages.uploadSourcePackageInfo, self.username, self.password, info) def _uploadPackageInfo(self, info): if self.use_session: return call(self.server.packages.uploadPackageInfoBySession, self.session.getSessionString(), info) return call(self.server.packages.uploadPackageInfo, self.username, self.password, info) def uploadHeaders(self): # Set the forcing factor self.setForce() # Relative directory self.setRelativeDir() # Set the count self.setCount() # set the org self.setOrg() # set the URL self.setURL() # set the channels self.setNoChannels() # set the server self.setServer() self.authenticate() source = self.options.source file_list = self._get_files() while file_list: chunk = file_list[:self.count] del file_list[:self.count] uploadedPackages, headersList = self._processBatch(chunk, relativeDir=self.relativeDir, source=self.options.source, verbose=self.options.verbose, nosig=self.options.nosig) if not headersList: # Nothing to do here... continue # Send the big hash info = {'packages': headersList} if self.orgId > 0 or self.orgId == '': info['orgId'] = self.orgId if self.force: info['force'] = self.force if self.channels: info['channels'] = self.channels # Some feedback if self.options.verbose: ReportError("Uploading batch:") for p in list(uploadedPackages.values())[0]: ReportError("\t\t%s" % p) if source: ret = self._uploadSourcePackageInfo(info) else: ret = self._uploadPackageInfo(info) if ret is None: self.die(-1, "Upload attempt failed") # Append the package information alreadyUploaded, newPackages = ret pkglists = (alreadyUploaded, newPackages) for idx, item in enumerate(pkglists): for p in item: key = tuple(p[:5]) if key not in uploadedPackages: # XXX Hmm self.warn(1, "XXX XXX %s" % str(p)) filename, checksum = uploadedPackages[key] # Some debugging if self.options.verbose: if idx == 0: pattern = "Already uploaded: %s" else: pattern = "Uploaded: %s" print(pattern % filename) # Per-package post actions # For backwards-compatibility with old spacewalk-proxy try: self.processPackage(p, filename, checksum) except TypeError: self.processPackage(p, filename) def processPackage(self, package, filename, checksum=None): pass def checkSession(self, session): return call(self.server.packages.check_session, session) def readSession(self): # pylint: disable=W0703 try: self.session = rhnpush_cache.RHNPushSession() self.session.readSession() except Exception: self.session = None def writeSession(self, session): if self.session: self.session.setSessionString(session) else: self.session = rhnpush_cache.RHNPushSession() self.session.setSessionString(session) if not self.options.no_session_caching: self.session.writeSession() def authenticate(self): # Only use the session token stuff if we're talking to a sat that supports session-token authentication. self.readSession() if self.session and not self.options.new_cache and self.options.username == self.username: chksession = self.checkSession(self.session.getSessionString()) if chksession: return self.setUsernamePassword() sessstr = call(self.server.packages.login, self.username, self.password) self.writeSession(sessstr) # set whether we should use checksum paths or not (if upstream supports # it we should). self.use_checksum_paths = hasChannelChecksumCapability(self.server) @staticmethod def _processFile(filename, relativeDir=None, source=None, nosig=None): """ Processes a file Returns a hash containing: header packageSize checksum relativePath nvrea """ # Is this a file? if not os.access(filename, os.R_OK): raise UploadError("Could not stat the file %s" % filename) if not os.path.isfile(filename): raise UploadError("%s is not a file" % filename) # Size size = os.path.getsize(filename) try: a_pkg = package_from_filename(filename) a_pkg.read_header() a_pkg.payload_checksum() assert a_pkg.header except: raise_with_tb(UploadError("%s is not a valid package" % filename), sys.exc_info()[2]) if nosig is None and not a_pkg.header.is_signed(): raise UploadError("ERROR: %s: unsigned rpm (use --nosig to force)" % filename) # Get the name, version, release, epoch, arch lh = [] for k in ['name', 'version', 'release', 'epoch']: if k == 'epoch' and not a_pkg.header[k]: # Fix the epoch lh.append(sstr("")) else: lh.append(sstr(a_pkg.header[k])) if source: lh.append('src') else: lh.append(sstr(a_pkg.header['arch'])) # Build the header hash to be sent info = {'header': Binary(a_pkg.header.unload()), 'checksum_type': a_pkg.checksum_type, 'checksum': a_pkg.checksum, 'packageSize': size, 'header_start': a_pkg.header_start, 'header_end': a_pkg.header_end} if relativeDir: # Append the relative dir too info["relativePath"] = "%s/%s" % (relativeDir, os.path.basename(filename)) info['nvrea'] = tuple(lh) return info def _processBatch(self, batch, relativeDir, source, verbose, nosig=None): sentPackages = {} headersList = [] for filename in batch: if verbose: print("Uploading %s" % filename) info = self._processFile(filename, relativeDir=relativeDir, source=source, nosig=nosig) # Get nvrea nvrea = info['nvrea'] del info['nvrea'] sentPackages[nvrea] = (filename, info['checksum']) # Append the header to the list of headers to be sent out headersList.append(info) return sentPackages, headersList def readStdin(): # Reads the standard input lines and returns a list l = [] while 1: line = sys.stdin.readline() if not line: break l.append(line.strip()) return l def getUsernamePassword(cmdlineUsername, cmdlinePassword): # Returns a username and password (either by returning the ones passed as # args, or the user's input if cmdlineUsername and cmdlinePassword: return cmdlineUsername, cmdlinePassword username = cmdlineUsername password = cmdlinePassword # Read the username, if not already specified tty = open("/dev/tty", "w") tty.write("SUSE Manager username: ") tty.close() tty = open("/dev/tty", "r") while not username: try: username = tty.readline() except KeyboardInterrupt: tty.write("\n") sys.exit(0) if username is None: # EOF tty.write("\n") sys.exit(0) username = username.strip() if username: break # Now read the password while not password: try: password = getpass.getpass("SUSE Manager password: ") except KeyboardInterrupt: tty.write("\n") sys.exit(0) tty.close() return username, password def listdir(directory): directory = os.path.abspath(os.path.normpath(directory)) if not os.access(directory, os.R_OK | os.X_OK): raise UploadError("Cannot read from directory %s" % directory) if not os.path.isdir(directory): raise UploadError("%s not a directory" % directory) # Build the package list packagesList = [] for f in os.listdir(directory): packagesList.append("%s/%s" % (directory, f)) return packagesList def call(function, *params, **kwargs): # Wrapper function try: ret = function(*params) except xmlrpclib.Fault: e = sys.exc_info()[1] x = parseXMLRPCfault(e) if x.faultString: print(x.faultString) if x.faultExplanation: print(x.faultExplanation) sys.exit(-1) except xmlrpclib.ProtocolError: e = sys.exc_info()[1] if kwargs.get('raise_protocol_error'): raise print(e.errmsg) sys.exit(-1) return ret def parseXMLRPCfault(fault): if not isinstance(fault, xmlrpclib.Fault): return None faultCode = fault.faultCode if faultCode and isinstance(faultCode, type(1)): faultCode = -faultCode return ServerFault(faultCode, "", fault.faultString) # pylint: disable=C0103 def listChannel(server, username, password, channels): return call(server.packages.listChannel, channels, username, password) def listChannelChecksum(server, username, password, channels): return call(server.packages.listChannelChecksum, channels, username, password) def listChannelBySession(server, session_string, channels): return call(server.packages.listChannelBySession, channels, session_string) def listChannelChecksumBySession(server, session_string, channels): return call(server.packages.listChannelChecksumBySession, channels, session_string) def listChannelSource(server, username, password, channels): return call(server.packages.listChannelSource, channels, username, password) def listChannelSourceBySession(server, session_string, channels): return call(server.packages.listChannelSourceBySession, channels, session_string) def listMissingSourcePackages(server, username, password, channels): return call(server.packages.listMissingSourcePackages, channels, username, password) def listMissingSourcePackagesBySession(server, session_string, channels): return call(server.packages.listMissingSourcePackagesBySession, channels, session_string) def getPackageChecksumBySession(server, session_string, info): return call(server.packages.getPackageChecksumBySession, session_string, info) def getSourcePackageChecksumBySession(server, session_string, info): return call(server.packages.getSourcePackageChecksumBySession, session_string, info) def getSourcePackageChecksum(server, username, password, info): return call(server.packages.getSourcePackageChecksum, username, password, info) # for backward compatibility with satellite <5.4.0 def getPackageMD5sumBySession(server, session_string, info): return call(server.packages.getPackageMD5sumBySession, session_string, info) def getSourcePackageMD5sumBySession(server, session_string, info): return call(server.packages.getSourcePackageMD5sumBySession, session_string, info) def getServer(uri, proxy=None, username=None, password=None, ca_chain=None): s = rpclib.Server(uri, proxy=proxy, username=username, password=password) if ca_chain: s.add_trusted_cert(ca_chain) return s # pylint: disable=E1123 def hasChannelChecksumCapability(rpc_server): """ check whether server supports getPackageChecksumBySession function""" # pylint: disable=W1505 if 'rpcServerOverride' in inspect.getargspec(rhnserver.RhnServer.__init__)[0]: server = rhnserver.RhnServer(rpcServerOverride=rpc_server) else: server = rhnserver.RhnServer() # pylint: disable=W0212 server._server = rpc_server return server.capabilities.hasCapability('xmlrpc.packages.checksums') def exists_getPackageChecksumBySession(rpc_server): """ check whether server supports getPackageChecksumBySession function""" # unfortunatelly we do not have capability for getPackageChecksumBySession function, # but extended_profile in version 2 has been created just 2 months before # getPackageChecksumBySession lets use it instead # pylint: disable=W1505 if 'rpcServerOverride' in inspect.getargspec(rhnserver.RhnServer.__init__)[0]: server = rhnserver.RhnServer(rpcServerOverride=rpc_server) else: server = rhnserver.RhnServer() # pylint: disable=W0212 server._server = rpc_server return server.capabilities.hasCapability('xmlrpc.packages.extended_profile', 2) # compare two package [n,v,r,e] tuples def packageCompare(pkg1, pkg2, is_mpm=None): if pkg1[0] != pkg2[0]: raise ValueError("You should only compare packages with the same name") packages = [] for pkg in (pkg1, pkg2): e = pkg[3] if e == "": e = None elif e is not None: e = str(e) evr = (e, str(pkg[1]), str(pkg[2])) packages.append(evr) if is_mpm: func = rhn_mpm.labelCompare else: from uyuni.common import rhn_rpm func = rhn_rpm.labelCompare return func(packages[0], packages[1]) # returns a header from a package file on disk. def get_header(filename, fildes=None, source=None): try: h = get_package_header(filename=filename, fd=fildes) except: raise_with_tb(UploadError("Package is invalid"), sys.exc_info()[2]) # Verify that this is indeed a binary/source. xor magic # xor doesn't work with None values, so compare the negated values - the # results are identical if (not source) ^ (not h.is_source): raise UploadError("Unexpected RPM package type") return h def ReportError(*args): sys.stderr.write(' '.join(map(str, args)) + "\n") 0707010000001D000081B400000000000000000000000161FA7D4F00000B4C000000000000000000000000000000000000001200000000mgr-push/utils.py# # Copyright (c) 2008--2017 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # import os import pwd import sys def get_home_dir(): userid = os.getuid() info = pwd.getpwuid(userid) return info[5] # If Object1 and Object2 have any common attributes, set the attribute in Object1 # to the value of the attribute in Object2. Does not make functions or variables starting with '_' equivalent. def make_common_attr_equal(object1, object2): # Go through every attribute in object1 for attr in object1.__dict__.keys(): # Make sure that the attribute name doesn't begin with "_" if len(attr) < 1 or attr[0] == "_": continue # Make sure that object2 has the attribute as well. and that it's not equal to ''. if attr not in object2.__dict__ or object2.__dict__[attr] == '': continue # Make sure the attributes are the same type OR that the attribute in object1 is None. if isinstance(object1.__dict__[attr], (type(object2.__dict__[attr]), type(None))): if object1.__dict__[attr] != object2.__dict__[attr]: object1.__dict__[attr] = object2.__dict__[attr] else: continue else: continue return (object1, object2) # Pylint is too stupid to understand subclasses of tuples apparently. # This is just to make it shut up. def tupleify_urlparse(urlparse_object): ret = [] if hasattr(urlparse_object, 'scheme'): ret.append(urlparse_object.scheme) ret.append(urlparse_object.netloc) ret.append(urlparse_object.path) ret.append(urlparse_object.params) ret.append(urlparse_object.query) ret.append(urlparse_object.fragment) else: ret = [urlparse_object[i] for i in range(0, 6)] if sys.version_info[0] == 3: for i in range(0, 6): if not isinstance(ret[i], str): ret[i] = ret[i].decode('ascii') return tuple(ret) if __name__ == "__main__": # This is just for testing purposes. # pylint: disable=R0903 class class1: def __init__(self): self.a = "aaaa" class class2: def __init__(self): self.a = 1 obj1 = class1() obj2 = class2() obj1, obj2 = make_common_attr_equal(obj1, obj2) print(obj1.a) print(obj2.a) 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor