File mgr-cfg-git-0.ba70d9e.obscpio of Package mgr-cfg
07070100000000000041FD000000000000000000000001625E850000000000000000000000000000000000000000000000000800000000mgr-cfg07070100000001000081B4000000000000000000000001625E8500000046AC000000000000000000000000000000000000001000000000mgr-cfg/LICENSE 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.
07070100000002000081B4000000000000000000000001625E850000000022000000000000000000000000000000000000001100000000mgr-cfg/Makefileinclude ../../../rel-eng/Makefile
07070100000003000081B4000000000000000000000001625E850000000943000000000000000000000000000000000000001600000000mgr-cfg/Makefile.defs# Common pathnames and programs for the Spacewalk project
#
# if not defined, definit as a noop
TOP ?= .
# global defines which control this build and where we deploy files
ROOT ?= /usr/share/rhn
export ROOT
PREFIX ?=
export PREFIX
BINDIR ?= /usr/bin
CONFDIR ?= /etc/sysconfig/rhn
# Compilation stuff
CC = gcc
PYTHON_INCLUDE = -I/usr/include/python$(PythonVersion)
CFLAGS = -Wall -O2 -fomit-frame-pointer $(PYTHON_INCLUDE) -fPIC
SOFLAGS = -shared -fPIC
# Installation stuff
INSTALL ?= /usr/bin/install -c --verbose
INSTALL_BIN ?= $(INSTALL) -m 755
INSTALL_DATA ?= $(INSTALL) -m 644
INSTALL_DIR ?= $(INSTALL) -m 755 -d
# This is for the subdir part
PYFILES = $(addsuffix .py,$(FILES))
# what do we need to install and where
INSTALL_FILES += $(PYFILES) $(PYCFILES)
INSTALL_DEST ?= $(ROOT)/$(SUBDIR)
DIRS += $(addprefix $(PREFIX), \
$(sort $(EXTRA_DIRS)) $(INSTALL_DEST))
LN ?= ln -sfv
all :: $(INSTALL_FILES)
install :: all $(DIRS) $(INSTALL_FILES)
@$(foreach f,$(INSTALL_FILES), \
$(INSTALL_DATA) $(f) $(PREFIX)$(INSTALL_DEST)/$(f) ; )
$(DIRS):
$(INSTALL_DIR) $@
clean ::
@rm -f *~ *.pyc *.pyo .??*~
@rm -f .\#*
@rm -f core
%.8 : %.sgml
[ -f /usr/bin/docbook2man ] && /usr/bin/docbook2man $< || true
# useful macro
descend-subdirs = @$(foreach d,$(SUBDIRS), $(MAKE) -C $(d) $@ || exit 1; )
# subdirs are treated at the end
all install clean:: $(SUBDIRS)
$(descend-subdirs)
# extra toy targets
# Python checker support
PYTHONPATH = $(TOP)
PYTHONVERSION ?= 2
PYCHECKER = pychecker
PYCHECKEROPTS = --maxreturns 10 --maxbranches 15
pychecker :: $(PYFILES)
@PYTHONPATH=$(PYTHONPATH) $(PYCHECKER) $(PYCHECKEROPTS) $(PYFILES) || :
$(descend-subdirs)
graphviz ::
@PYTHONPATH=$(PYTHONPATH) $(PYCHECKER) -Z $(PYCHECKEROPTS) $(PYFILES) || exit 0
make_rel = $(shell a="$1"; b=; while [ "$$a" != '/' ]; do \
a=$$(dirname $$a); \
if [ -z "$$b" ]; then b=".."; else b="$$b/.."; fi; \
done; echo $$b)
install-bin: $(PREFIX)$(BINDIR) $(addsuffix .py,$(EXEC_FILES))
@$(foreach f,$(EXEC_FILES), \
chmod 755 $(PREFIX)$(INSTALL_DEST)/$(f).py; \
$(LN) $(call make_rel,$(BINDIR))$(INSTALL_DEST)/$(f).py $(PREFIX)$(BINDIR)/$(f)-$(PYTHONVERSION); )
install-conf: $(PREFIX)$(CONFDIR) $(CONFIG_FILES)
@$(foreach f,$(CONFIG_FILES), \
$(INSTALL_DATA) $(f) $(PREFIX)$(CONFDIR)/$(f) ; )
07070100000004000081B4000000000000000000000001625E850000000375000000000000000000000000000000000000001800000000mgr-cfg/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 = systemsmanagement/uyuni/master/docker/containers/uyuni-master
DOCKER_REGISTRY = registry.opensuse.org
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-cfg; 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
07070100000005000081B4000000000000000000000001625E8500000000C0000000000000000000000000000000000000001800000000mgr-cfg/Makefile.rhncfg# Makefile for spacewalk backend
#
# what is the backend top dir
TOP = .
# Specific stuff
SUBDIRS = actions config_common config_client config_management
include $(TOP)/Makefile.defs
07070100000006000041FD000000000000000000000001625E850000000000000000000000000000000000000000000000001000000000mgr-cfg/actions07070100000007000081B4000000000000000000000001625E85000000042E000000000000000000000000000000000000001900000000mgr-cfg/actions/Makefile# Makefile for spacewalk backend
#
# what is the backend top dir
TOP = ..
# Specific stuff
SUBDIR = rhn/actions
EXEC_FILES = rhn-actions-control
FILES = $(EXEC_FILES) configfiles script \
Modes ModeController ModeControllerCreator
CAPS = configfiles script
CAPS_DIR = /etc/sysconfig/rhn/clientCaps.d
#man page stuff
DOCBOOK = $(wildcard /usr/bin/docbook2man)
SGMLS = $(wildcard *.sgml)
MANS = $(patsubst %.sgml,%.8,$(SGMLS))
MANDIR ?= /usr/man
EXTRA_DIRS = $(MANDIR)/man8 $(BINDIR) $(CAPS_DIR)
include $(TOP)/Makefile.defs
install :: install-bin install-caps
install-caps :: $(CAPS) $(PREFIX)$(CAPS_DIR)
$(INSTALL_DATA) $(CAPS) $(PREFIX)$(CAPS_DIR)
ifneq ($(DOCBOOK),)
# install man pages
all :: $(MANS)
install :: $(MANS) $(PREFIX)$(MANDIR)
$(INSTALL_DATA) $(MANS) $(PREFIX)$(MANDIR)/man8
endif
clean ::
@rm -fv $(MANS) manpage.*
07070100000008000081B4000000000000000000000001625E850000001496000000000000000000000000000000000000002200000000mgr-cfg/actions/ModeController.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
try:
PY3 = sys.version_info.major >= 3
raw_input = input
except AttributeError:
PY3 = False
class ModeMissingException(Exception):
pass
#Handles operations on a group of Modes.
class ModeController:
def __init__(self, force=None):
self.mode_list = {} #Indexed on the name of the mode
self.force = False
#Enable the mode.
def on(self, mode_name):
if mode_name in self.mode_list:
self.mode_list[mode_name].on()
else:
raise ModeMissingException()
#Disable the mode
def off(self, mode_name):
if mode_name in self.mode_list:
self.mode_list[mode_name].off()
else:
raise ModeMissingException()
#Turn on all of the modes.
def all_on(self):
for m in self.mode_list.keys():
self.mode_list[m].on()
if self.mode_list['all'].is_on():
self.mode_list['all'].off()
#Turn off all of the modes.
def all_off(self):
for m in self.mode_list.keys():
self.mode_list[m].off()
#Check to see if the mode is on.
def is_on(self, mode_name):
if mode_name in self.mode_list:
return self.mode_list[mode_name].is_on()
else:
return 0
#Check to see if the mode is off.
def is_off(self, mode_name):
if mode_name in self.mode_list:
return self.mode_list[mode_name].is_off()
else:
return 0
#Add another mode to the batch.
def add_mode(self, mode_obj):
mode_name = mode_obj.get_name()
if not mode_name in self.mode_list:
self.mode_list[mode_name] = mode_obj
#Remove a mode from the batch.
def del_mode(self, mode_obj):
mode_name = mode_obj.get_name()
if mode_name in self.mode_list:
del self.mode_list[mode_name]
#set the value of force
def set_force(self, force):
self.force = force
class ConfigFilesModeController(ModeController):
def __init__(self):
ModeController.__init__(self)
def is_on(self, mode_name):
if ModeController.is_on(self, 'all'):
return 1
else:
return ModeController.is_on(self, mode_name)
def is_off(self, mode_name):
if ModeController.is_off(self, 'all'):
return 1
else:
return ModeController.is_off(self, mode_name)
#the possible presence of the 'all' file confuses things a little.
#If the user enables something while the 'all' file is present, nothing should be added to the configfiles dir.
def on(self, mode_name):
if mode_name != 'all':
if self.is_off('all'):
ModeController.on(self, mode_name)
#Go through each of the modes and see if they're on. If they're all on, then place the 'all' file in there.
all_modes_on = 1
for m in self.mode_list.keys():
if m != 'all' and self.mode_list[m].is_off():
all_modes_on = 0
if all_modes_on:
self.all_on()
else:
self.all_on()
#If the 'all' file is present and the user disables a mode, then the all file must be removed, and all modes other than
#the specified mode need to be turned on.
def off(self, mode_name):
if mode_name != 'all':
if self.is_on('all'):
if not self.force:
ask_before_continuing("All modes are currently enabled. Continue? (y or n):")
#Turn off all modes
self.all_off()
#Manually flip on all of the modes except 'all'.
for m in self.mode_list.keys():
if m != 'all':
self.mode_list[m].on()
#Turn off the specified mode. Calls off() at the Mode level, not at the Controller level.
self.mode_list[mode_name].off()
else:
self.all_off()
#This is a little different when the 'all' file is used.
#There shouldn't be any other files in the directory when 'all' is used.
def all_on(self):
#Get rid of all of the files.
self.all_off()
#Turn on the 'all' mode.
self.mode_list['all'].on()
def ask_before_continuing(question=None):
if question is None:
the_question = "Continue? (y or n):"
else:
the_question = question
answer = '-1'
while answer != 'y' and answer != 'n':
answer = raw_input(the_question)
answer = answer.lower()[0]
if answer == 'n':
sys.exit(0)
07070100000009000081B4000000000000000000000001625E8500000009E7000000000000000000000000000000000000002900000000mgr-cfg/actions/ModeControllerCreator.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 ModeController
import Modes
class ModeControllerCreator:
#if mode_list isn't set in the constructor, the populate_list is going to have to be called before create_controller.
def __init__(self, mode_list=None):
self.mode_list = mode_list or []
#A reference to a class obj. This is the type of controller that will be returned by create_controller.
self.controller_class = ModeController.ModeController
#Sets the class that the controller will be instantiated as. The constructor for the class shouldn't have
#to take any parameters.
def set_controller_class(self, controller_class):
self.controller_class = controller_class
#Populates self.mode_list with concrete implementations of Modes.
def populate_list(self, mode_list):
self.mode_list = mode_list
#using the Modes in self.mode_list, create, populate, and return a ModeController
def create_controller(self):
controller = self.controller_class()
for m in self.mode_list:
controller.add_mode(m)
return controller
def get_controller_creator():
if sys.platform.find('sunos') > -1:
mode_list = [Modes.SolarisDeployMode(), Modes.SolarisDiffMode(), Modes.SolarisUploadMode(), Modes.SolarisMTimeUploadMode(), Modes.SolarisAllMode()]
else:
mode_list = [Modes.DeployMode(), Modes.DiffMode(), Modes.UploadMode(), Modes.MTimeUploadMode(), Modes.AllMode()]
controller = ModeControllerCreator(mode_list=mode_list)
controller.set_controller_class(ModeController.ConfigFilesModeController)
return controller
def get_run_controller_creator():
if sys.platform.find('sunos') > -1:
mode_list = [Modes.SolarisRunMode(), Modes.SolarisRunAllMode()]
else:
mode_list = [Modes.RunMode(), Modes.RunAllMode()]
controller = ModeControllerCreator(mode_list=mode_list)
return controller
0707010000000A000081B4000000000000000000000001625E8500000017BA000000000000000000000000000000000000001900000000mgr-cfg/actions/Modes.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 os
#Base class for all modes.
class BaseMode:
def __init__(self):
self.state = False
self.name = ""
def on(self):
self.state = True
def off(self):
self.state = False
def is_on(self):
if self.state:
return True
else:
return False
def is_off(self):
if not self.state:
return True
else:
return False
def set_name(self, name):
self.name = name
def get_name(self):
return self.name
#Contains the directory and file manipulation stuff
class PathHandler:
def __init__(self):
self.rhn_root = "/etc/sysconfig/rhn/allowed-actions/configfiles"
#Set the rhn_root variable.
def set_rhn_root(self, rhn_root):
self.rhn_root = rhn_root
#Creates the self.rhn_root directories if they don't already exist. This allows subclasses to implement modes in different locations.
def _create_rhnconfig_path(self):
if not os.path.exists(self.rhn_root):
os.makedirs(self.rhn_root, int('0770', 8))
#Create the file if it doesn't already exist.
def add_file(self, filename):
self._create_rhnconfig_path()
if not self.check_for_file(filename):
try:
f = open(os.path.join(self.rhn_root, filename), "w")
f.close()
except Exception:
raise
#remove the file if it's present.
def remove_file(self, filename):
self._create_rhnconfig_path()
if self.check_for_file(filename):
try:
os.remove(os.path.join(self.rhn_root, filename))
except Exception:
raise
#Returns True if filename exists in /etc/sysconfig/rhn/allowed-actions/configfiles
def check_for_file(self, filename):
self._create_rhnconfig_path()
return os.path.exists(os.path.join(self.rhn_root, filename))
#Stuff that's common to the Mode subclasses.
class ConfigFilesBaseMode(BaseMode):
def __init__(self):
BaseMode.__init__(self)
self.ph = PathHandler()
self.name = None #Must be set in subclass
def on(self):
self.ph.add_file(self.name)
self.state = True
def off(self):
self.ph.remove_file(self.name)
self.state = False
#Could probably just check the value of state...
def is_on(self):
return self.ph.check_for_file(self.name)
def is_off(self):
if self.ph.check_for_file(self.name):
return False
elif not self.ph.check_for_file(self.name):
return True
class RunMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "run"
self.ph.set_rhn_root("/etc/sysconfig/rhn/allowed-actions/script")
class RunAllMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "all"
self.ph.set_rhn_root("/etc/sysconfig/rhn/allowed-actions/script")
class AllMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "all"
class DeployMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "deploy"
class DiffMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "diff"
class UploadMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "upload"
class MTimeUploadMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "mtime_upload"
#Solaris Specific Modes
class SolarisRunMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "run"
self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/script")
class SolarisAllRunMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "all"
self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/script")
class SolarisAllMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "all"
self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")
class SolarisDeployMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "deploy"
self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")
class SolarisDiffMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "diff"
self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")
class SolarisUploadMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "upload"
self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")
class SolarisMTimeUploadMode(ConfigFilesBaseMode):
def __init__(self):
ConfigFilesBaseMode.__init__(self)
self.name = "mtime_upload"
self.ph.set_rhn_root("/opt/redhat/rhn/solaris/etc/sysconfig/rhn/allowed-actions/configfiles")
0707010000000B000081B4000000000000000000000001625E850000000266000000000000000000000000000000000000001C00000000mgr-cfg/actions/__init__.py#
# Copyright (c) 2008--2013 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.
#
0707010000000C000081B4000000000000000000000001625E850000000081000000000000000000000000000000000000001C00000000mgr-cfg/actions/configfilesconfigfiles.mtime_upload(1)=1
configfiles.upload(1)=1
configfiles.deploy(1)=1
configfiles.diff(1)=1
configfiles.base64_enc(1)=1
0707010000000D000081FD000000000000000000000001625E8500000047F6000000000000000000000000000000000000001F00000000mgr-cfg/actions/configfiles.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 os
import time
import sys
from config_common import local_config, file_utils, utils, cfg_exceptions
from config_common.rhn_log import set_debug_level, get_debug_level, set_logfile, log_to_file
from config_common.transactions import DeployTransaction, FailedRollback
from config_client import rpc_cli_repository
from up2date_client import config
from uyuni.common.usix import StringType, UnicodeType, PY3
# this is a list of the methods that get exported by a module
__rhnexport__ = [
'mtime_upload',
'upload',
'deploy',
'verify',
'diff',
]
# action version we understand
ACTION_VERSION = 2
# when we do this within up2date, should just have something like
# __rhn_require_local_permission__ that flags the methods requiring the touched
# file
_permission_root_dir = '/etc/sysconfig/rhn/allowed-actions'
def _local_permission_check(action_type):
# action_type ala configfiles.deploy
atype_structure = action_type.split('.')
for i in range(len(atype_structure)):
all_structure = atype_structure[:i]
all_structure.append('all')
if os.path.exists(os.path.join(_permission_root_dir, "script/all")) and action_type == "script.run":
os.rename(os.path.join(_permission_root_dir, "script/all"),os.path.join(_permission_root_dir, "script/run"))
potential_all_path = os.path.join(*all_structure)
if os.path.exists(os.path.join(_permission_root_dir, potential_all_path)):
return 1
action_path = os.path.join(*atype_structure)
return os.path.exists(os.path.join(_permission_root_dir, action_path))
def _perm_error(action_type):
return (42, "Local permission not set for action type %s" % action_type, {})
def _visit_dir(params, dirname, names):
matches = params['matches']
info = params['info']
ignore_dirs = params['ignore']
now = params['now']
i = 0
while i < len(names):
full_path = os.path.join(dirname, names[i])
is_dir = os.path.isdir(full_path)
if is_dir:
if full_path in ignore_dirs:
# don't consider the entire subtree on subsequent runs of
# visit
del names[i]
else:
i = i + 1
# since we can have multiple search paths hitting the same subdir,
# filter 'em out after the first pass
ignore_dirs[full_path] = None
continue
if not os.path.exists(full_path):
i = i + 1
continue
mtime = os.path.getmtime(full_path)
# do it via delta...
if (now - mtime) <= (info['now'] - info['start_date']):
if info['end_date']:
if (now - mtime) >= (info['now'] - info['end_date']):
matches.append(full_path)
else:
matches.append(full_path)
i = i + 1
def format_result(result, files):
files_too_large = result.get('files_too_large') or []
quota_failed = result.get('failed_due_to_quota') or []
missing_files = result.get('missing_files') or []
extras = { 'attempted_paths' : files }
if missing_files:
extras['missing_files'] = missing_files
if files_too_large:
extras['files_too_large'] = files_too_large
if quota_failed:
extras['quota_failed'] = quota_failed
num_files = len(files)
num_uploaded = num_files - (len(missing_files) + len(files_too_large) + len(quota_failed))
if num_uploaded == num_files:
return 0, "All files successfully uploaded", extras
else:
return -1, "Some files failed to upload", extras
## foo = {'ignore': ['\n', '/home/bretm/rhn/build/', '/home/bretm/rhn/sql'],
## 'info': {'now': 1071722611.0, 'import_contents': 'N', 'start_date': 1071290580.0, 'end_date': ''},
## 'search': ['/home/bretm/'],
## }
def mtime_upload(action_id, params, cache_only=None):
if cache_only:
return (0, "no-ops for caching", {})
action_type = 'configfiles.mtime_upload'
if not _local_permission_check(action_type):
log_to_file(0, "permissions error: " + str(action_type))
return _perm_error(action_type)
_init()
file_matches = []
now = time.time()
upload_contents = None
ignore_dirs = {'/proc':None, '/dev':None}
if params['info']['import_contents'] == 'Y':
upload_contents = 1
for to_ignore in params['ignore']:
ignore_dirs[utils.normalize_path(to_ignore)] = 1
for search_path in params['search']:
for dirname, dirs, names in os.walk(utils.normalize_path(search_path)):
_visit_dir({
'matches' : file_matches,
'info' : params['info'],
'ignore' : ignore_dirs,
'now' : now,
}, dirname, names)
if not file_matches:
return 0, "No files found", {}
r = rpc_cli_repository.ClientRepository()
result = r.put_files(action_id, file_matches, upload_contents=upload_contents)
formatted_result = format_result(result, file_matches)
log_to_file(0, formatted_result)
return formatted_result
def upload(action_id, params, cache_only=None):
if cache_only:
return (0, "no-ops for caching", {})
action_type = 'configfiles.upload'
if not _local_permission_check(action_type):
log_to_file(0, "permissions error: " + str(action_type))
return _perm_error(action_type)
_init()
files = params or []
r = rpc_cli_repository.ClientRepository()
result = r.put_files(action_id, files)
formatted_result = format_result(result, files)
log_to_file(0, formatted_result)
return formatted_result
def deploy(params, topdir=None, cache_only=None):
if cache_only:
return (0, "no-ops for caching", {})
action_type = 'configfiles.deploy'
if not _local_permission_check(action_type):
log_to_file(0, "permissions error: " + str(action_type))
return _perm_error(action_type)
_init()
files = params.get('files') or []
dep_trans = DeployTransaction(transaction_root=topdir, auto_rollback=0)
for file in files:
dep_trans.add(file)
try:
dep_trans.deploy()
#5/3/05 wregglej - 135415 Adding stuff for missing user info
except cfg_exceptions.UserNotFound:
e = sys.exc_info()[1]
try:
dep_trans.rollback()
except FailedRollback:
log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ))
return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {})
#5/3/05 wregglej - 136415 Adding some more exceptions to handle
except cfg_exceptions.UserNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ))
return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
#5/5/05 wregglej - 136415 Adding exception handling for unknown group,
except cfg_exceptions.GroupNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
else:
log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ))
return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {})
except cfg_exceptions.GroupNotFound:
e = sys.exc_info()[1]
try:
dep_trans.rollback()
except FailedRollback:
log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ))
return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {})
#5/3/05 wregglej - 136415 Adding some more exceptions to handle
except cfg_exceptions.UserNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ) )
return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
#5/5/05 wregglej - 136415 Adding exception handling for unknown group,
except cfg_exceptions.GroupNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
else:
log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (e[0], ))
return (51, "Failed deployment and rollback, group '%s' could not be found" % (e[0], ), {})
except cfg_exceptions.FileEntryIsDirectory:
e = sys.exc_info()[1]
try:
dep_trans.rollback()
except FailedRollback:
log_to_file(0, "Failed deployment and rollback, %s already exists as a directory" % (e[0], ))
return (44, "Failed deployment and rollback, %s already exists as a directory" % (e[0], ), {})
#5/3/05 wregglej - 136415 Adding some more exceptions to handle
except cfg_exceptions.UserNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ))
return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
#5/5/05 wregglej - 136415 Adding exception handling for unknown group,
except cfg_exceptions.GroupNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
else:
log_to_file(0, "Failed deployment, %s already exists as a directory" % (e[0], ))
return (45, "Failed deployment, %s already exists as a directory" % (e[0], ), {})
except cfg_exceptions.DirectoryEntryIsFile:
e = sys.exc_info()[1]
try:
dep_trans.rollback()
except FailedRollback:
log_to_file(0, "Failed deployment and rollback, %s already exists as a file" % (e[0], ))
return (46, "Failed deployment and rollback, %s already exists as a file" % (e[0], ), {})
#5/3/05 wregglej - 136415 Adding exceptions for missing user
except cfg_exceptions.UserNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ))
return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {})
#5/5/05 wregglej - 136415 Adding exception handling for unknown group,
except cfg_exceptions.GroupNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
else:
log_to_file(0, "Failed deployment, %s already exists as a file" % (e[0], ))
return (47, "Failed deployment, %s already exists as a file" % (e[0], ), {})
except Exception:
e = sys.exc_info()[1]
print(e)
try:
dep_trans.rollback()
except FailedRollback:
e2 = sys.exc_info()[1]
log_to_file(0, "Failed deployment, failed rollback: %s" % e2)
return (48, "Failed deployment, failed rollback: %s" % e2, {})
#5/3/05 wregglej - 135415 Add exception handling for missing user.
except cfg_exceptions.UserNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0]))
return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0]), {})
#5/5/05 wregglej - 136415 Adding exception handling for unknown group,
except cfg_exceptions.GroupNotFound:
f = sys.exc_info()[1]
log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],))
return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {})
else:
log_to_file(0, "Failed deployment, rolled back: %s" % e)
return (49, "Failed deployment, rolled back: %s" % e, {})
extras = {}
log_to_file(0, "Files successfully deployed: %s %s" % (format_file_string(files, create_key_list()), str(extras)))
return 0, "Files successfully deployed", extras
def diff(params, cache_only=None):
def is_utf8(in_string):
"""Returns true if input is a valid UTF-8 string, False otherwise."""
if isinstance(in_string, UnicodeType):
return True
elif isinstance(in_string, StringType):
if PY3:
return True
try:
in_string.decode('utf-8')
return True
except UnicodeDecodeError:
return False
return False
if cache_only:
return (0, "no-ops for caching", {})
action_type = 'configfiles.diff'
if not _local_permission_check(action_type):
log_to_file(0, "permissions error: " + str(action_type))
return _perm_error(action_type)
_init()
files = params.get('files') or []
fp = file_utils.FileProcessor()
missing_files = []
diffs = {}
exists = hasattr(os.path, 'lexists') and os.path.lexists or os.path.exists
for file in files:
path = file['path']
if not exists(path):
missing_files.append(path)
continue
diff = fp.diff(file)
if diff:
diffs[path] = diff
extras = {}
if missing_files:
extras['missing_files'] = missing_files
if diffs:
for file in diffs.keys():
if not is_utf8(diffs[file]):
diffs[file] = "%s: binary files differ" % file
extras['diffs'] = diffs
log_to_file(0, "Files successfully compared: %s %s" % (format_file_string(files, create_key_list()), str(extras)))
return 0, "Files successfully compared", extras
verify = diff
#The format_file_string and create_key_list functions can be used together to create a string
#containing information about the files in file_list. Use sparingly.
#file_list is a list of dictionaries containing file information.
#keylist is a list of strings containing the keys of the information in file_list that you wish to print out.
def format_file_string(file_list, keylist):
outstr = ""
for afile in file_list:
outstr
for key in keylist:
formatstr = "\n%s: %s"
if key in afile:
outstr = outstr + formatstr % (key, afile[key])
outstr = outstr + "\n"
return outstr
#Returns a list of strings. Each string is a key in the dictionary containing file information.
#The number of keys returned corresponds to the debug_level. The higher the debug_level, the longer the
#list of keys.
def create_key_list():
#The list of keys. The order of the keys determines what debug_level they will be returned in.
#For example, at debug level 0 only the path and revision will be included. At level 1, the path, revision,
#config_channel, and filemode keys should be included.
key_list = [
'path',
'revision',
'config_channel',
'filemode',
'filetype',
'encoding',
'username',
'groupname',
'delim_start',
'delim_end',
'md5sum',
'checksum_type',
'checksum',
'verify_contents',
'file_contents',
]
#This dictionary associates each debug level (the key) with the index into key_list (the value) at which
#we should stop including keys in the returned list.
debug_levels = {
0 : 2,
1 : 4,
2 : 6,
3 : 8,
4 : 10,
5 : 15,
}
curr_debug = get_debug_level()
if curr_debug > 5:
curr_debug = 5
if curr_debug < 0:
curr_debug = 0
if not curr_debug in debug_levels.keys():
curr_debug = 0
return key_list[:debug_levels[curr_debug]]
def _init():
cfg = config.initUp2dateConfig()
cfg_dict = dict(cfg.items())
server_url = config.getServerlURL()
cfg_dict['proto'], cfg_dict['server_name'] = utils.parse_url(server_url[0], scheme="https")[:2]
if len(server_url) > 1:
cfg_dict['server_list'] = server_url
local_config.init('rhncfg-client', defaults=cfg_dict)
set_debug_level(int(local_config.get('debug_level') or 0))
set_logfile("/var/log/rhncfg-actions")
0707010000000E000081FD000000000000000000000001625E85000000115A000000000000000000000000000000000000002700000000mgr-cfg/actions/rhn-actions-control.py#!/usr/bin/python
#
# 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 ModeControllerCreator
from optparse import Option, OptionParser
def main():
optionsTable = [
Option('--enable-deploy', action='store_true', help='Allow rhncfg-client to deploy files.', default=0),
Option('--enable-diff', action='store_true', help='Allow rhncfg-client to diff files.', default=0),
Option('--enable-upload', action='store_true', help='Allow rhncfg-client to upload files.', default=0),
Option('--enable-mtime-upload', action='store_true', help='Allow rhncfg-client to upload mtime.', default=0),
Option('--enable-run', action='store_true', help='Allow rhncfg-client the ability to execute remote scripts.', default=0),
Option('--enable-all', action='store_true', help='Allow rhncfg-client to do everything.', default=0),
Option('--disable-deploy', action='store_true', help='Disable deployment.', default=0),
Option('--disable-diff', action='store_true', help='Disable diff.', default=0),
Option('--disable-upload', action='store_true', help='Disable upload.', default=0),
Option('--disable-mtime-upload',action='store_true', help='Disable mtime upload.', default=0),
Option('--disable-run', action='store_true', help='Disable remote script execution.', default=0),
Option('--disable-all', action='store_true', help='Disable all options.', default=0),
Option('-f', '--force', action='store_true', help='Force the operation without confirmation', default=0),
Option('--report', action='store_true', help='Report the status of the mode settings (enabled or disabled)', default=0),
]
parser = OptionParser(option_list=optionsTable)
(options, args) = parser.parse_args()
creator = ModeControllerCreator.get_controller_creator()
controller = creator.create_controller()
controller.set_force(options.force)
runcreator = ModeControllerCreator.get_run_controller_creator()
runcontroller = runcreator.create_controller()
runcontroller.set_force(options.force)
if options.enable_deploy:
controller.on('deploy')
if options.enable_diff:
controller.on('diff')
if options.enable_upload:
controller.on('upload')
if options.enable_mtime_upload:
controller.on('mtime_upload')
if options.enable_all:
controller.all_on()
runcontroller.all_on()
if options.enable_run:
runcontroller.on('run')
runcontroller.off('all')
if options.disable_deploy:
controller.off('deploy')
if options.disable_diff:
controller.off('diff')
if options.disable_upload:
controller.off('upload')
if options.disable_mtime_upload:
controller.off('mtime_upload')
if options.disable_all:
controller.all_off()
runcontroller.all_off()
if options.disable_run:
runcontroller.off('run')
runcontroller.off('all')
if options.report:
mode_list = ['deploy', 'diff', 'upload', 'mtime_upload']
for m in mode_list:
rstring = "%s is %s"
status = "disabled"
if controller.is_on(m):
status = "enabled"
print(rstring % (m, status))
status = "disabled"
if runcontroller.is_on('all'):
runcontroller.off('all')
runcontroller.on('run')
if runcontroller.is_on('run'):
status = "enabled"
print(rstring % ('run', status))
if __name__ == "__main__":
try:
sys.exit(main() or 0)
except KeyboardInterrupt:
sys.stderr.write("user interrupted\n")
sys.exit(0)
0707010000000F000081B4000000000000000000000001625E850000000FA1000000000000000000000000000000000000002900000000mgr-cfg/actions/rhn-actions-control.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!ENTITY RHNACTIONSCONTROL "Spacewalk Actions Control" >
]>
<refentry>
<RefMeta>
<RefEntryTitle>rhn-actions-control</RefEntryTitle><manvolnum>8</manvolnum>
<RefMiscInfo>Version 4.0</RefMiscInfo>
</RefMeta>
<RefNameDiv>
<RefName><command>rhn-actions-control</command></RefName>
<RefPurpose>
Controls the enabling and disabling of scheduled actions.
</RefPurpose>
</RefNameDiv>
<RefSynopsisDiv>
<Synopsis>
<cmdsynopsis>
<command>rhn-actions-control</command>
<arg>options <replaceable>...</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<arg>--enable-deploy | --disable-deploy</arg>
</cmdsynopsis>
<cmdsynopsis>
<arg>--enable-diff | --disable-diff</arg>
</cmdsynopsis>
<cmdsynopsis>
<arg>--enable-upload | --disable-upload</arg>
</cmdsynopsis>
<cmdsynopsis>
<arg>--enable-mtime-upload | --disable-mtime-upload</arg>
</cmdsynopsis>
<cmdsynopsis>
<arg>--enable-run | --disable-run</arg>
</cmdsynopsis>
<cmdsynopsis>
<arg>--enable-all | --disable-all</arg>
</cmdsynopsis>
<cmdsynopsis>
<arg>--report</arg>
</cmdsynopsis>
</Synopsis>
</RefSynopsisDiv>
<RefSect1><Title>Description</Title>
<para>
The &RHNACTIONSCONTROL; (<emphasis>rhn-actions-control</emphasis>)
allows the user to enable and disable different types of scheduled
actions. These various modes are enabled/disabled by placing/removing
files in the <emphasis>/etc/sysconfig/rhn/allowed-actions/</emphasis>
directory. Due to the default permissions on the
<emphasis>/etc/sysconfig/rhn/</emphasis> directory, &RHNACTIONSCONTROL;
will most likely have to be run by someone with root access.
</para>
</RefSect1>
<RefSect1><Title>Options</Title>
<variablelist>
<varlistentry>
<term>--enable-deploy, --disable-deploy</term>
<listitem>
<para>Enable/disable the deploy scheduled action. I.e., allow
installation of configuration files from the central repository
on the system. Without this functionality enabled, the
configuration management interface provides no real value for
this system.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--enable-diff, --disable-diff</term>
<listitem>
<para>Enable/disable the diff scheduled action.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--enable-upload, --disable-upload</term>
<listitem>
<para>Enable/disable the upload scheduled action.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--enable-mtime-upload, --disable-mtime-upload</term>
<listitem>
<para>Enable/disable the mtime-upload scheduled action.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--enable-run, --disable-run</term>
<listitem>
<para>Enable/disable the ability to run remote scripts.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--enable-all, --disable-all</term>
<listitem>
<para>Enable/disable all capabilities.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-f, --force</term>
<listitem>
<para>Force an operation without confirmation.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--report</term>
<listitem>
<para>Report the status of the mode settings (enabled or disabled).</para>
</listitem>
</varlistentry>
</variablelist>
</RefSect1>
<RefSect1><Title>Authors</Title>
<simplelist>
<member>John Wregglesworth <email>wregglej@redhat.com</email></member>
<member>Todd Warner <email>taw@redhat.com</email> (man page only)</member>
</simplelist>
</RefSect1>
</RefEntry>
07070100000010000081B4000000000000000000000001625E850000000010000000000000000000000000000000000000001700000000mgr-cfg/actions/scriptscript.run(1)=1
07070100000011000081B4000000000000000000000001625E8500000022BD000000000000000000000000000000000000001A00000000mgr-cfg/actions/script.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 sys
import pwd
import grp
import time
import select
import signal
import tempfile
import base64
try:
MAXFD = os.sysconf("SC_OPEN_MAX")
except:
MAXFD = 256
# this is ugly, hopefully it will be natively supported in up2date
from rhn.actions.configfiles import _local_permission_check, _perm_error
from config_common import local_config
from config_common.rhn_log import set_logfile, log_to_file
from up2date_client import config
# this is a list of the methods that get exported by a module
__rhnexport__ = [
'run',
]
# action version we understand
ACTION_VERSION = 2
def _create_script_file(script, uid=None, gid=None):
storageDir = tempfile.gettempdir()
script_path = os.path.join(storageDir, 'rhn-remote-script')
# Loop a couple of times to try to get rid of race conditions
for i in range(2):
try:
fd = os.open(script_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, int("0700", 8))
# If this succeeds, break out the loop
break
except OSError:
e = sys.exc_info()[1]
if e.errno != 17: # File exists
raise
# File does exist, try to remove it
try:
os.unlink(script_path)
except OSError:
e = sys.exc_info()[1]
if e.errno != 2: # No such file or directory
raise
else:
# Tried a couple of times, failed; bail out raising the latest error
raise
sf = os.fdopen(fd, 'wb')
sf.write(script.encode("utf-8"))
sf.close()
if uid and gid:
os.chown(script_path, uid, gid)
return script_path
# Make sure the dir-path to a file exists
def _create_path(fpath):
d = os.path.dirname(fpath)
if d and not os.path.exists(d):
os.makedirs(d, int("0700", 8))
return os.path.exists(d)
def run(action_id, params, cache_only=None):
cfg = config.initUp2dateConfig()
local_config.init('rhncfg-client', defaults=dict(cfg.items()))
tempfile.tempdir = local_config.get('script_tmp_dir')
logfile_name = local_config.get('script_log_file')
log_output = local_config.get('script_log_file_enable')
if log_output:
# If we're going to log, make sure we can create the logfile
_create_path(logfile_name)
if cache_only:
return (0, "no-ops for caching", {})
action_type = 'script.run'
if not _local_permission_check(action_type):
return _perm_error(action_type)
extras = {'output':''}
script = params.get('script')
if not script:
return (1, "No script to execute", {})
username = params.get('username')
groupname = params.get('groupname')
if not username:
return (1, "No username given to execute script as", {})
if not groupname:
return (1, "No groupname given to execute script as", {})
timeout = params.get('timeout')
if timeout:
try:
timeout = int(timeout)
except ValueError:
return (1, "Invalid timeout value", {})
else:
timeout = None
db_now = params.get('now')
if not db_now:
return (1, "'now' argument missing", {})
db_now = time.mktime(time.strptime(db_now, "%Y-%m-%d %H:%M:%S"))
now = time.time()
process_start = None
process_end = None
child_pid = None
# determine uid/ugid for script ownership, uid also used for setuid...
try:
user_record = pwd.getpwnam(username)
except KeyError:
return 1, "No such user %s" % username, extras
uid = user_record[2]
ugid = user_record[3]
# create the script on disk
try:
script_path = _create_script_file(script, uid=uid, gid=ugid)
except OSError:
e = sys.exc_info()[1]
return 1, "Problem creating script file: %s" % e, extras
# determine gid to run script as
try:
group_record = grp.getgrnam(groupname)
except KeyError:
return 1, "No such group %s" % groupname, extras
run_as_gid = group_record[2]
# create some pipes to communicate w/ the child process
(pipe_read, pipe_write) = os.pipe()
process_start = time.time()
child_pid = os.fork()
if not child_pid:
# Parent doesn't write to child, so close that part
os.close(pipe_read)
# Redirect both stdout and stderr to the pipe
os.dup2(pipe_write, sys.stdout.fileno())
os.dup2(pipe_write, sys.stderr.fileno())
# Close unnecessary file descriptors (including pipe since it's duped)
for i in range(3, MAXFD):
try:
os.close(i)
except:
pass
# all scripts initial working directory will be /
# puts burden on script writer to ensure cwd is correct within the
# script
os.chdir('/')
# the child process gets the desired uid/gid
os.setgid(run_as_gid)
groups=[g.gr_gid for g in grp.getgrall() if username in g.gr_mem or username in g.gr_name]
os.setgroups(groups)
os.setuid(uid)
# give this its own process group (which happens to be equal to its
# pid)
os.setpgrp()
clean_env = {"PATH": "/sbin:/bin:/usr/sbin:/usr/bin", "TERM": "xterm"}
# Finally, exec the script
try:
os.umask(int("022", 8))
os.execve(script_path, [script_path, ], clean_env)
finally:
# This code can be reached only when script_path can not be
# executed as otherwise execv never returns.
# (The umask syscall always succeeds.)
os._exit(1)
# Parent doesn't write to child, so close that part
os.close(pipe_write)
output = None
timed_out = None
out_stream = open('/var/lib/up2date/action.%s' % str(action_id), 'ab+', 0)
while 1:
select_wait = None
if timeout:
elapsed = time.time() - process_start
if elapsed >= timeout:
timed_out = 1
# Send TERM to all processes in the child's process group
# Send KILL after that, just to make sure the child died
os.kill(-child_pid, signal.SIGTERM)
time.sleep(2)
os.kill(-child_pid, signal.SIGKILL)
break
select_wait = timeout - elapsed
try:
input_fds, output_fds, error_fds = select.select([pipe_read], [], [], select_wait)
except select.error:
return 255, "Termination signal occurred during execution.", {}
if error_fds:
# when would this happen?
os.close(pipe_read)
return 1, "Fatal exceptional case", extras
if not (pipe_read in input_fds):
# Read timed out, should be caught in the next loop
continue
output = os.read(pipe_read, 4096)
if not output:
# End of file from the child
break
out_stream.write(output)
os.close(pipe_read)
# wait for the child to complete
(somepid, exit_status) = os.waitpid(child_pid, 0)
process_end = time.time()
# Copy the output from the temporary file
out_stream.seek(0, 0)
extras['output'] = out_stream.read()
out_stream.close()
# Log script-output locally, unless we're asked not to
if log_output :
set_logfile(logfile_name)
log_to_file(0, extras['output'])
# since output can contain chars that won't make xmlrpc very happy,
# base64 encode it...
extras['base64enc'] = 1
extras['output'] = base64.encodestring(extras['output'])
extras['return_code'] = exit_status
# calculate start and end times in db's timespace
extras['process_start'] = db_now + (process_start - now)
extras['process_end'] = db_now + (process_end - now)
for key in ('process_start', 'process_end'):
extras[key] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(extras[key]))
# clean up the script
os.unlink(script_path)
if timed_out:
return 1, "Script killed, timeout of %s seconds exceeded" % timeout, extras
if exit_status == 0:
return 0, "Script executed", extras
return 1, "Script failed", extras
07070100000012000041FD000000000000000000000001625E850000000000000000000000000000000000000000000000001600000000mgr-cfg/config_client07070100000013000081B4000000000000000000000001625E850000000386000000000000000000000000000000000000001F00000000mgr-cfg/config_client/Makefile# Makefile for spacewalk backend
#
# what is the backend top dir
TOP = ..
# Specific stuff
SUBDIR = config_client
EXEC_FILES = rhncfg-client
FILES = $(EXEC_FILES) __init__ handler_base \
rhncfgcli_channels \
rhncfgcli_diff rhncfgcli_get \
rhncfgcli_list \
rhncfgcli_elist \
rhncfgcli_verify rpc_cli_repository
CONFIG_FILES = rhncfg-client.conf
# man pages
DOCBOOK = $(wildcard /usr/bin/docbook2man)
SGMLS = $(wildcard *.sgml)
MANS = $(patsubst %.sgml,%.8,$(SGMLS))
MANDIR ?= /usr/man
EXTRA_DIRS = $(BINDIR) $(CONFDIR) $(MANDIR)/man8
include $(TOP)/Makefile.defs
ifneq ($(DOCBOOK),)
# install man pages
all :: $(MANS)
install :: $(MANS) $(PREFIX)/$(MANDIR)
$(INSTALL_DATA) $(MANS) $(PREFIX)/$(MANDIR)/man8
clean ::
@rm -fv $(MANS) manpage.*
endif
install :: install-bin install-conf
07070100000014000081B4000000000000000000000001625E850000000266000000000000000000000000000000000000002200000000mgr-cfg/config_client/__init__.py#
# Copyright (c) 2008--2013 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.
#
07070100000015000081B4000000000000000000000001625E8500000008D4000000000000000000000000000000000000002800000000mgr-cfg/config_client/cli_repository.py#!/usr/bin/python
#
# 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 os
from config_common import cfg_exceptions, repository
# this is a bit odd; right now, we instantiate a regular
# repository.Repository object since it does a lot of local disk
# access for us. In the future, though, it is unlikely Repository and
# ClientRepository classes/APIs will actually share this in common
# since Repository mostly requires user perms and ClientRepository
# mostly requires server perms
class ClientRepository:
def __init__(self):
self.server_repository = repository.Repository()
tmp_channels = os.environ.get("RHNCFG_CHANNELS") or "all"
# listed in order of losers first, ie, entry 2 overrides entry
# 1, etc
self.config_channels = tmp_channels.split(":")
self.cfg_files = {}
def list_files(self):
# iterate over channels, accumulating hash of what files
# come from where; subsequent entries override previous ones,
# so the final hash is the result we seek
if self.cfg_files:
return self.cfg_files
self.cfg_files = {}
for ns in self.config_channels:
for file in self.server_repository.list_files(ns):
self.cfg_files[file] = [ ns, file ]
return self.cfg_files
def get_file(self, file):
if not self.cfg_files:
raise "never did a list_files"
if file not in self.cfg_files:
raise cfg_exceptions.ConfigNotManaged(file)
return self.server_repository.get_file(self.cfg_files[file][0], self.cfg_files[file][1])
def list_config_channels(self):
return self.config_channels
07070100000016000081B4000000000000000000000001625E850000000AEB000000000000000000000000000000000000002600000000mgr-cfg/config_client/handler_base.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 os
import os.path
from config_common import handler_base, utils
from config_common.rhn_log import log_debug
class HandlerBase(handler_base.HandlerBase):
def get_dest_file(self, file):
target_dir = os.sep
return utils.normalize_path(target_dir + os.sep + file)
# Returns a list of valid files
def get_valid_files(self):
files_hash = {}
for file in self.repository.list_files():
files_hash[file[1]] = None
if not self.args:
# No file specified; use all of them
files = list(files_hash.keys())
files.sort()
return files
files = []
for file in self.args:
#5/9/05 wregglej - 151197 make sure that trailing /'s aren't passed through for directories.
if os.path.isdir(file):
if file[-1] == "/":
file = file[0:-1]
if file not in files_hash:
print("Not found on server: %s" % file)
continue
files.append(file)
files.sort()
return files
# Main function to be run
def run(self):
log_debug(2)
for file in self.get_valid_files():
(src, file_info, dirs_created) = self.repository.get_file_info(file)
ftype = file_info.get('filetype')
if not src:
continue
dst = self.get_dest_file(file)
self._process_file(src, dst, file, ftype, file_info)
# To be overridden with specific actions in subclasses
def _process_file(self, *args):
pass
class TopdirHandlerBase(HandlerBase):
_options_table = [
HandlerBase._option_class(
'--topdir', action="store",
help="Make all file operations relative to this directory.",
),
HandlerBase._option_class(
'--exclude', action="append",
help="Excludes a file from being deployed with 'get'. May be used multiple times.",
),
]
def get_dest_file(self, file):
target_dir = self.options.topdir or os.sep
return utils.normalize_path(target_dir + os.sep + file)
07070100000017000081B4000000000000000000000001625E8500000005B2000000000000000000000000000000000000002900000000mgr-cfg/config_client/rhncfg-client.conf[rhncfg-client]
# don't change this
systemid = /etc/sysconfig/rhn/systemid
# increase for debugging output
debug_level = 0
# Default directory for remote command temporary script file.
# If not set it will default to None and it will fallback to python's default tempdir search algorithm
script_tmp_dir = /var/spool/rhn
# don't change this... used in substitutions below.
# if you get rid of the '%(server_handler)s' bits below,
# the *MUST* be replaced with this value...
server_handler = /CONFIG-MANAGEMENT
# don't change this, period.
repository_type = rpc_cli_repository
# to use a server other than what up2date is configured to use,
# do something along the lines of:
# server_url = https://some.example.com%(server_handler)s
# server_url = http://another.example.net:8080%(server_handler)s
# server_url = https://yet.another.example.org:8081/CONFIG-MANAGEMENT
server_url = https://%(server_name)s%(server_handler)s
# the following fields are inherited from up2date's configuration,
# but are overridable in this file
# enableProxy = 1
# enableProxyAuth = 1
# httpProxy = some.proxy.example.com:3030
# proxyUser = proxy_user_name
# proxyPassword = proxy_password
# sslCACert = /usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT
# control of remote-script-logging
# Should we store output of a RemoteCommand locally? 1 = yes, 0 = no
script_log_file_enable = 0
# Where will we write that output?
script_log_file = /var/log/rhn/rhncfg-action-output.log
07070100000018000081B4000000000000000000000001625E8500000004F1000000000000000000000000000000000000002700000000mgr-cfg/config_client/rhncfg-client.py#!/usr/bin/python
#
# Copyright (c) 2008--2012 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, os
from config_common.rhn_main import BaseMain
class Main(BaseMain):
modes = ['diff', 'get', 'list', 'elist', 'channels', 'verify']
repository_class_name = 'ClientRepository'
plugins_dir = 'config_client'
config_section = 'rhncfg-client'
mode_prefix = 'rhncfgcli'
if __name__ == '__main__':
# quick check to see if you are a super-user.
if os.getuid() != 0:
sys.stderr.write("ERROR: must be root to execute!" + "\n")
sys.exit(1)
try:
sys.exit(Main().main() or 0)
except KeyboardInterrupt:
sys.stderr.write("user interrupted\n")
sys.exit(0)
07070100000019000081B4000000000000000000000001625E850000001408000000000000000000000000000000000000002900000000mgr-cfg/config_client/rhncfg-client.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!ENTITY RHN "Spacewalk" >
<!ENTITY RHNCFGC "rhncfg-client" >
]>
<refentry>
<RefMeta>
<RefEntryTitle>&RHNCFGC;</RefEntryTitle><manvolnum>8</manvolnum>
<RefMiscInfo>Version 4.0</RefMiscInfo>
</RefMeta>
<RefNameDiv>
<RefName><command>&RHNCFGC;</command></RefName>
<RefPurpose>
a tool used for system configuration querying and deployment.
</RefPurpose>
</RefNameDiv>
<RefSynopsisDiv>
<Synopsis>
<cmdsynopsis>
<command>&RHNCFGC;</command>
<arg>mode </arg>
<arg>options <replaceable>...</replaceable></arg>
<arg>arguments <replaceable>...</replaceable></arg>
</cmdsynopsis>
</Synopsis>
</RefSynopsisDiv>
<RefSect1><Title>Description</Title>
<para>
The &RHN; Configuration Client (<emphasis>&RHNCFGC;</emphasis>) is used to query a
system's configuration makeup and deployment status as managed by &RHN;.
</para>
<para>
This tool enables a &RHN; client to <emphasis>list</emphasis> available
configuration files, fetch (or <emphasis>get</emphasis>) those
configuration files, <emphasis>view</emphasis> configuration channels, and
finally, <emphasis>diff</emphasis> and <emphasis>verify</emphasis> current
configuration of the system in comparison to the system's status according
to &RHN;.
</para>
<para>
<simplelist>
<member>This tool utilizes a "cvs style" command syntax: command mode options</member>
<member>For example: &RHNCFGC; diff --topdir /home/test/blah/</member>
</simplelist>
</para>
</RefSect1>
<RefSect1><Title>mode & options</Title>
<variablelist>
<varlistentry>
<term>list</term>
<listitem>
<para>
List the configuration files for the machine and the labels
of the config channels containing them. Used alone.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>elist</term>
<listitem>
<para>
List the configuration files for the machine and the labels
of the config channels containing them which provides an
ls-esque style output. Used alone.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>get <replaceable>CONFIG_FILEPATH</replaceable></term>
<listitem>
<para>
Download/deploy the most relevant configuration file for the
machine. For example: &RHNCFGC; get /etc/foo.conf
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>channels</term>
<listitem>
<para>
View the labels and names of the config channels that apply to
the system. Used alone.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>diff</term>
<listitem>
<para>
Obtain a difference between the config files deployed on the
system and those for it stored by &RHN;.
</para>
<para>
May be used with the --topdir <replaceable>ROOT_PATH</replaceable>
option. E.g., &RHNCFGC; diff --topdir /home/test/foo/
</para>
<para>
If a file is readable only by root then the differences are not
displayed by default. The option -d or --diff-secure-files
forces the differences to be displayed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>verify</term>
<listitem>
<para>
Quick verification to see if files are different than those
associated with it on &RHN;.
</para>
<para>
All files are displayed unless called with the -o or --only
option, in which case only files with differences are displayed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-h, --help</term>
<listitem>
<para>Display the help screen with a list of options.</para>
</listitem>
</varlistentry>
</variablelist>
</RefSect1>
<RefSect1><Title>Examples/Summary</Title>
<simplelist>
<member><command>&RHNCFGC; list</command></member>
<member><command>&RHNCFGC; elist</command></member>
<member><command>&RHNCFGC; get /etc/foo.conf</command></member>
<member><command>&RHNCFGC; channels</command></member>
<member><command>&RHNCFGC; diff</command></member>
<member><command>&RHNCFGC; diff --topdir /tmp/configroot/</command></member>
<member><command>&RHNCFGC; verify</command></member>
</simplelist>
</RefSect1>
<RefSect1><Title>See Also</Title>
<simplelist>
<member>rhncfg-manager (8)</member>
</simplelist>
</RefSect1>
<RefSect1><Title>Authors</Title>
<simplelist>
<member>Mihai Ibanescu <email>misa@redhat.com</email></member>
<member>Bret McMillan <email>bretm@redhat.com</email></member>
<member>Todd Warner <email>taw@redhat.com</email> (man page only)</member>
</simplelist>
</RefSect1>
</RefEntry>
0707010000001A000081B4000000000000000000000001625E8500000004A3000000000000000000000000000000000000002500000000mgr-cfg/config_client/rhncfgcli.conf[rhncfgcli]
# Should we display the diff of a file that is only readable by root? By
# default we do not, uncomment this line to change that.
#display_diff = True
# don't change this
systemid = /etc/sysconfig/rhn/systemid
# increase for debugging output
debug_level = 0
# don't change this... used in substitutions below.
# if you get rid of the '%(server_handler)s' bits below,
# the *MUST* be replaced with this value...
server_handler = /CONFIG-MANAGEMENT
# don't change this, period.
repository_type = rpc_cli_repository
# to use a server other than what up2date is configured to use,
# do something along the lines of:
# server_url = https://some.example.com%(server_handler)s
# server_url = http://another.example.net:8080%(server_handler)s
# server_url = https://yet.another.example.org:8081/CONFIG-MANAGEMENT
server_url = https://%(server_name)s%(server_handler)s
# the following fields are inherited from up2date's configuration,
# but are overridable in this file
# enableProxy = 1
# enableProxyAuth = 1
# httpProxy = some.proxy.example.com:3030
# proxyUser = proxy_user_name
# proxyPassword = proxy_password
# sslCACert = /usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT
0707010000001B000081B4000000000000000000000001625E8500000004E1000000000000000000000000000000000000002C00000000mgr-cfg/config_client/rhncfgcli_channels.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.
#
from config_common.rhn_log import log_debug
import handler_base
class Handler(handler_base.HandlerBase):
def run(self):
log_debug(2)
r = self.repository
config_channels = r.load_config_channels()
if not config_channels:
print("(no config channels associated with this system)")
return
print("Config channels:")
templ = "%-32s%s"
label = "Label"
name = "Name"
print(templ % (label, name))
print(templ % ('-' * len(label), '-' * len(name)))
for c in config_channels:
print(templ % (c['label'], c['name']))
0707010000001C000081B4000000000000000000000001625E850000000C5E000000000000000000000000000000000000002800000000mgr-cfg/config_client/rhncfgcli_diff.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 sys
import os
import handler_base
from config_common.file_utils import diff
from config_common.local_config import get as get_config
class Handler(handler_base.HandlerBase):
_options_table = [
handler_base.HandlerBase._option_class(
'-d', '--diff-secure-files', dest='display_diff',
action="store_true", default=False,
help="Force diff to display the diff for secure files.",
)
]
_usage_options = handler_base.HandlerBase._usage_options + " [ files ... ]"
def _process_file(self, *args):
src, dst= args [:2]
type = args[3]
file_info = args[4]
if type == 'symlink':
if not os.path.exists(dst):
print("Symbolic link '%s' is missing" % dst)
return
if not os.path.islink(dst):
print("Path '%s' is not a symbolic link" % dst)
return
#dst is a symlink, so just tell the user we're skipping the entry
srclink = os.readlink(src)
destlink = os.readlink(dst)
if srclink != destlink:
print("Symbolic links differ. Channel: '%s' -> '%s' System: '%s' -> '%s' " % (dst,srclink, dst, destlink))
elif type == 'file':
if 'is_binary' in file_info and file_info['is_binary'] == 'Y':
src_content = dst_content = None
content_differs = False
src_file = open(src, 'rb')
src_content = src_file.read()
src_file.close()
if os.access(dst, os.R_OK):
dst_file = open(dst, 'rb')
dst_content = dst_file.read()
dst_file.close()
else:
print("File %s that is not readable. Re-deployment of configuration file is recommended." % dst)
return
if len(src_content) != len(dst_content):
content_differs = True
else:
for i in range(len(src_content)):
if src_content[i] != dst_content[i]:
content_differs = True
break
if content_differs:
sys.stdout.write("Binary file %s differs.\n" % (dst))
else:
sys.stdout.write(''.join(diff(src, dst, srcname=dst, dstname=dst,
display_diff=
(self.options.display_diff or get_config('display_diff')))))
0707010000001D000081FD000000000000000000000001625E85000000071B000000000000000000000000000000000000002900000000mgr-cfg/config_client/rhncfgcli_elist.pyfrom config_common.rhn_log import log_debug, die
from config_common.file_utils import ostr_to_sym
import handler_base, base64
import sys
from rhn.stringutils import bstr
class Handler(handler_base.HandlerBase):
def run(self):
log_debug(2)
r = self.repository
files = r.list_files()
if not files:
die(1, "No managed files.")
label = "Config Channel"
maxlen = max([len(s[0]) for s in files])
maxlen = max(maxlen, len(label)) + 2
print("%-10s %8s %-8s %10s %+3s %*s %s" % ('Mode', 'Owner', 'Group', 'Size', 'Rev', maxlen, label, "File"))
arg_files = []
if len(sys.argv) > 2:
arg_files = sys.argv[2:len(sys.argv)]
for file in files:
if len(arg_files) and not file[1] in arg_files:
continue
# Get the file info
finfo = r.get_file_info(file[1])[1]
# Get the file length
if finfo['encoding'] == 'base64':
fsize = len(base64.decodestring(bstr(finfo['file_contents'])))
else:
# * indicates raw 'unencoded' size
fsize = '*' + str(len(finfo['file_contents']))
if finfo['filetype'] == 'symlink':
permstr = ostr_to_sym('777', finfo['filetype'])
dest = "%s -> %s" % (file[1], finfo['symlink'])
fsize = str(len(finfo['symlink']))
finfo['username'] = 'root'
finfo['groupname'] = 'root'
else:
permstr = ostr_to_sym(finfo['filemode'], finfo['filetype']) or ''
dest = file[1]
print("%10s %8s %-8s %10s %+3s %*s %s" % (permstr, finfo['username'], finfo['groupname'], fsize, finfo['revision'], maxlen, file[0], dest))
0707010000001E000081B4000000000000000000000001625E8500000003E6000000000000000000000000000000000000002700000000mgr-cfg/config_client/rhncfgcli_get.py#
# Copyright (c) 2008--2013 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 handler_base
from config_common.deploy import deploy_files
class Handler(handler_base.TopdirHandlerBase):
_usage_options = handler_base.HandlerBase._usage_options + " [ files ... ]"
def run(self):
deploy_files(self.options.topdir,
self.repository,
self.get_valid_files(),
self.options.exclude)
0707010000001F000081B4000000000000000000000001625E8500000006F6000000000000000000000000000000000000002800000000mgr-cfg/config_client/rhncfgcli_list.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.
#
from config_common.rhn_log import log_debug, die
import handler_base
import sys
class Handler(handler_base.HandlerBase):
def run(self):
log_debug(2)
r = self.repository
files = r.list_files()
if not files:
die(1, "No managed files.")
label = "Config Channel"
maxlen = max([len(s[0]) for s in files])
maxlen = max(maxlen, len(label)) + 2
print("DoFoS %*s %s" % (maxlen, label, "File"))
arg_files = []
if len(sys.argv) > 2:
arg_files = sys.argv[2:len(sys.argv)]
for file in files:
if len(arg_files) and not file[1] in arg_files:
continue
# checking to see if the filetype is in the 'file' entry,
# and if it is and that type is '1', it is a file
if (len(file) < 3) or file[2] == 1:
print("F %*s %s" % (maxlen, file[0], file[1]))
elif file[2] == 2 :
# the filetype is a directory
print("D %*s %s" % (maxlen, file[0], file[1]))
else:
print("S %*s %s" % (maxlen, file[0], file[1]))
07070100000020000081B4000000000000000000000001625E8500000031E2000000000000000000000000000000000000002A00000000mgr-cfg/config_client/rhncfgcli_verify.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.
#
from config_common import utils
from config_common.rhn_log import log_debug
import handler_base
import os
import stat
import pwd, grp
try:
from selinux import lgetfilecon
except:
# on rhel4 we do not support selinux
def lgetfilecon(path):
return [0, '']
class Handler(handler_base.HandlerBase):
_usage_options = handler_base.HandlerBase._usage_options + " [ files ... ]"
_options_table = [
handler_base.HandlerBase._option_class(
'--verbose',
"-v",
action="count",
help="Increase the amount of output detail.",
),
handler_base.HandlerBase._option_class(
'--only',
"-o",
action="count",
help="Only show files that differ.",
),
]
# Main function to be run
def run(self):
log_debug(2)
ret = []
#Labels for column headers
status_label = "STATUS"
owner_status = "OWNER"
group_status = "GROUP"
mode_status = "MODE"
selinux_status = "SELINUX"
file_status = "FILE"
status_help = "(channel:local)"
maxlenarr = {
'status' : len(status_label),
'owner' : max(len(owner_status), len(status_help)),
'group' : max(len(group_status), len(status_help)),
'mode' : max(len(mode_status), len(status_help)),
'selinux' : max(len(selinux_status), len(status_help)),
}
#Iterate throught the files and process them. The src file is the file as it is in the config channel,
#the dst file is the file as it is in the filesystem.
for file in self.get_valid_files():
(src, file_info, dirs_created) = self.repository.get_file_info(file)
ftype = file_info.get('filetype')
if not src:
continue
dst = self.get_dest_file(file)
#Added file_info parameter, which contains information needed to look for differences in the owner, group, and mode.
ret_dict = self._process_file(src, dst, file, ftype, file_info)
if self.options.verbose:
#Get the max of the return values for this file, which is used to determine the length of each field in the output.
#Don't include the 'file' value, because it gets displayed last in each row and will throw off the size of the other fields.
maxlenarr['status'] = max(maxlenarr['status'], len(ret_dict['status']))
maxlenarr['owner'] = max(maxlenarr['owner'], len(ret_dict['owner']))
maxlenarr['group'] = max(maxlenarr['group'], len(ret_dict['group']))
maxlenarr['mode'] = max(maxlenarr['mode'], len(ret_dict['mode']))
if len(ret_dict['selinux']) > 0:
(src, dst) = ret_dict['selinux'].split('|')
maxlenarr['selinux'] = max(maxlenarr['selinux'], len(src), len(dst))
#Place the return values into a list so we can iterate through them later when we want to print them out.
ret.append(ret_dict)
if self.options.verbose:
formatstr = "%-*s" #format string for the fields where the length matters.
formatstr_nolimit = "%-s" #format string for the fields where the length of the field doesn't matter. Namely, the file field.
#The overall format of the output.
outstring = "%(status)s %(owner)s %(group)s %(mode)s %(selinux)s %(file)s"
#Print out the column labels.
print(outstring % {
"status" : formatstr % (maxlenarr['status'], status_label),
"owner" : formatstr % (maxlenarr['owner'], owner_status),
"group" : formatstr % (maxlenarr['group'], group_status),
"mode" : formatstr % (maxlenarr['mode'], mode_status),
"selinux" : formatstr % (maxlenarr['selinux'], selinux_status),
"file" : formatstr_nolimit % (file_status),
})
print(outstring % {
"status" : formatstr % (maxlenarr['status'], ""),
"owner" : formatstr % (maxlenarr['owner'], status_help),
"group" : formatstr % (maxlenarr['group'], status_help),
"mode" : formatstr % (maxlenarr['mode'], status_help),
"selinux" : formatstr % (maxlenarr['selinux'], status_help),
"file" : ""
})
#Go through each of the dictionaries returned by self._process_file(), format their values, and print out the result.
for fdict in ret:
src_selinux = dst_selinux = ""
if len(fdict['selinux']) > 0:
(src_selinux, dst_selinux) = fdict['selinux'].split('|')
if self.options.only:
sum = 0
for key in fdict.keys():
if key != 'file':
sum += len(fdict[key])
if sum == 0:
continue
print(outstring % {
"status" : formatstr % (maxlenarr['status'], fdict['status']),
"owner" : formatstr % (maxlenarr['owner'], fdict['owner']),
"group" : formatstr % (maxlenarr['group'], fdict['group']),
"mode" : formatstr % (maxlenarr['mode'], fdict['mode']),
"selinux" : formatstr % (maxlenarr['selinux'], src_selinux),
"file" : formatstr_nolimit % (fdict['file']),
})
if len(dst_selinux) > 0:
print(outstring % {
"status" : formatstr % (maxlenarr['status'], ""),
"owner" : formatstr % (maxlenarr['owner'], ""),
"group" : formatstr % (maxlenarr['group'], ""),
"mode" : formatstr % (maxlenarr['mode'], ""),
"selinux" : formatstr % (maxlenarr['selinux'], dst_selinux),
"file" : "",
})
#Not verbose, so give the simple output for each file...
else:
outstring = "%*s %s"
maxlen = max([0] + [len(x['status']) for x in ret]) + 1
for fdict in ret:
if self.options.only and len(fdict['status']) == 0:
continue
print(outstring % (maxlen, fdict['status'], fdict['file']))
def _process_file(self, *args):
owner_report = "%s:%s"
group_report = "%s:%s"
perm_report = "%s:%s"
selinux_report = "%s|%s"
src, dst, file, type, info = args[:5]
owner_status = ""
group_status = ""
perm_status = ""
selinux_status = ""
status = []
stat_err = 0
#Stat the destination file
try:
dst_stat = os.lstat(dst)
except:
stat_err = 1
if type != 'symlink':
src_user = info['username']
if not stat_err:
#check for owner differences
dst_uid = dst_stat[stat.ST_UID]
try:
dst_user = pwd.getpwuid(dst_uid)[0]
except KeyError:
# Orphan UID with no name,return unknown
dst_user = "unknown(UID %d)" % (dst_uid,)
else:
dst_user = "missing"
#owner_status gets displayed with the verbose option.
if src_user == dst_user:
owner_status = ""
else:
owner_status = owner_report % (src_user, dst_user)
status.append('user')
src_group = info['groupname']
if not stat_err:
#check for group differences
dst_gid = dst_stat[stat.ST_GID]
try:
dst_group = grp.getgrgid(dst_gid)[0]
except KeyError:
# Orphan GID with no name,return unknown
dst_group = "unknown(GID %d)" % (dst_gid,)
else:
dst_group = "missing"
#group_status gets displayed with the verbose option.
if src_group == dst_group:
group_status = ""
else:
group_status = group_report % (src_group, dst_group)
status.append('group')
#check for permissions differences
src_perm = str(info['filemode'])
if not stat_err:
#The mode returned by stat is decimal, but won't match the value in file_info unless it's octal.
#Unfortunately, the mode in file_info looks like the octal value of the mode, except it's in decimal.
#The solution I came up with is to convert them both into strings, rip off the leading '0' from the
#mode returned by stat, use the resulting strings. It sucks, but it seems to work (for now).
dst_perm = str(oct(stat.S_IMODE(dst_stat[stat.ST_MODE])))
else:
dst_perm = "missing"
#rip off the leading '0' from the mode returned by stat()
if dst_perm[0] == '0':
dst_perm = dst_perm[1:]
#perm_status gets displayed with the verbose option.
if src_perm == dst_perm:
perm_status = ""
else:
perm_status = perm_report % (src_perm, dst_perm)
status.append('mode')
# compare selinux contexts
if 'selinux_ctx' in info:
src_selinux = info['selinux_ctx']
if src_selinux:
if not stat_err:
try:
dst_selinux = lgetfilecon(dst)[1]
except OSError:
dst_selinux = ""
if dst_selinux == None:
dst_selinux = ""
else:
dst_selinux = "missing"
if src_selinux == dst_selinux:
selinux_status = ""
else:
selinux_status = selinux_report % (src_selinux, dst_selinux)
status.append('selinux')
#figure out the ultimate value of status.
if stat_err:
status = ["missing"]
elif type == 'symlink':
if not os.path.islink(file):
status = ["missing"]
elif os.readlink(file) != info['symlink']:
status.append('target-link-modified')
elif type == 'directory':
if not os.path.isdir(file):
status = ["missing"]
elif not os.access(dst, os.R_OK):
status = ["missing"]
else:
src_sum = utils.sha256_file(src)
dst_sum = utils.sha256_file(dst)
if src_sum != dst_sum:
status.append('modified')
return {
"status" : ','.join(status),
"owner" : owner_status,
"group" : group_status,
"mode" : perm_status,
"selinux" : selinux_status,
"file" : file,
}
07070100000021000081B4000000000000000000000001625E850000001829000000000000000000000000000000000000002C00000000mgr-cfg/config_client/rpc_cli_repository.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 sys
try: # python2
import xmlrpclib
except ImportError: # python3
import xmlrpc.client as xmlrpclib
from config_common import local_config, cfg_exceptions, file_utils, \
repository
from config_common.rhn_log import log_debug
from uyuni.common.usix import raise_with_tb
import traceback
class ClientRepository(repository.RPC_Repository):
default_systemid = "/etc/sysconfig/rhn/systemid"
# bug #170825,169203: reusing the base class's default value for setup_network
def __init__(self, setup_network=1):
repository.RPC_Repository.__init__(self, setup_network)
systemid_file = local_config.get("systemid") or self.default_systemid
try:
f = open(systemid_file, "r")
except IOError:
e = sys.exc_info()[1]
sys.stderr.write("Cannot open %s: %s\n" % (systemid_file, e))
sys.exit(1)
self.system_id = f.read()
f.close()
log_debug(4, 'system id', self.system_id)
self.files_to_delete = []
def rpc_call(self, method_name, *params):
try:
result = repository.RPC_Repository.rpc_call(self, method_name, *params)
except xmlrpclib.Fault:
e = sys.exc_info()[1]
if e.faultCode == -9:
# System not subscribed
raise_with_tb(cfg_exceptions.AuthenticationError(
"Invalid digital server certificate%s" % e.faultString), sys.exc_info()[2])
raise
return result
def load_config_channels(self):
log_debug(4)
self.config_channels = self.rpc_call(
'config.client.list_config_channels', self.system_id)
return self.config_channels
def list_files(self):
log_debug(4)
return self.rpc_call('config.client.list_files', self.system_id)
def get_file_info(self, file, auto_delete=1, dest_directory=None):
log_debug(4, file)
result = self.rpc_call('config.client.get_file', self.system_id, file)
if 'missing' in result:
return None
dirs_created = None
# Older servers will not return directories; if filetype is missing,
# assume file
if result.get('filetype') == 'directory':
if dest_directory:
result['path'] = dest_directory.rstrip(os.path.sep) + result['path']
if os.path.isfile(result['path']):
raise cfg_exceptions.DirectoryEntryIsFile(result['path'])
else:
auto_delete = 0
temp_file = result['path']
else:
f = file_utils.FileProcessor()
temp_file, dirs_created = f.process(result, directory=dest_directory)
if auto_delete:
self.files_to_delete.append(temp_file)
return temp_file, result, dirs_created
def put_files(self, action_id, files, upload_contents=1):
"""Inserts a set of files into the repo, as a result of a scheduled
action"""
log_debug(4)
missing_files = []
files_too_large = []
failed_due_to_quota = []
max_file_size = self.get_maximum_file_size()
for file in files:
try:
params = self._make_file_info(file, local_path=None,
load_contents=upload_contents)
except cfg_exceptions.RepositoryLocalFileError:
missing_files.append(file)
continue
if upload_contents and (params['size'] > max_file_size):
files_too_large.append(file)
continue
try:
self.rpc_call('config.client.upload_file',
self.system_id, action_id, params)
except xmlrpclib.Fault:
e = sys.exc_info()[1]
fault_code, fault_string = e.faultCode, e.faultString
# deal with particular faults
if fault_code == -4003:
# File too large
files_too_large.append(file)
elif fault_code == -4014:
# Ran out of org quota space
failed_due_to_quota.append(file)
else:
raise_with_tb(cfg_exceptions.RepositoryFilePushError(fault_code,
fault_string), sys.exc_info()[2])
except Exception:
traceback.print_exc()
raise
result = {}
# If there are files too large to be pushed, result will have a key
# `file_too_large'
if len(files_too_large) > 0:
result['files_too_large'] = files_too_large
if len(failed_due_to_quota) > 0:
result['failed_due_to_quota'] = failed_due_to_quota
if len(missing_files) > 0:
result['missing_files'] = missing_files
return result
def list_config_channels(self):
log_debug(4)
return self.config_channels
def _get_default_delimiters(self):
"retrieves the default delimiters from the server"
log_debug(4)
result = self.rpc_call('config.client.get_default_delimiters',
self.system_id)
return result.get('delim_start'), result.get('delim_end')
def _get_maximum_file_size(self):
log_debug(4)
result = self.rpc_call('config.client.get_maximum_file_size',
self.system_id)
return result
def cleanup(self):
log_debug(4)
for file in self.files_to_delete:
os.unlink(file)
07070100000022000041FD000000000000000000000001625E850000000000000000000000000000000000000000000000001600000000mgr-cfg/config_common07070100000023000081B4000000000000000000000001625E85000000016B000000000000000000000000000000000000001F00000000mgr-cfg/config_common/Makefile# Makefile for spacewalk backend
#
# what is the backend top dir
TOP = ..
# Specific stuff
SUBDIR = config_common
FILES = __init__ cfg_exceptions \
file_utils deploy \
handler_base local_config \
repository rhn_log rhn_main \
utils transactions rpc_wrapper
include $(TOP)/Makefile.defs
07070100000024000081B4000000000000000000000001625E850000000266000000000000000000000000000000000000002200000000mgr-cfg/config_common/__init__.py#
# Copyright (c) 2008--2013 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.
#
07070100000025000081B4000000000000000000000001625E8500000006FE000000000000000000000000000000000000002800000000mgr-cfg/config_common/cfg_exceptions.py#
# Copyright (c) 2008--2013 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.
#
class InvalidSession(Exception):
pass
class AuthenticationError(Exception):
pass
class ConfigChannelNotInRepo(Exception):
pass
class ConfigChannelAlreadyExistsError(Exception):
pass
class ConfigChannelNotEmptyError(Exception):
pass
class ConfigNotManaged(Exception):
pass
class ConfigurationError(Exception):
pass
class BinaryFileDiffError(Exception):
pass
class RepositoryFileError(Exception):
pass
class RepositoryLocalFileError(Exception):
pass
class RepositoryFileMissingError(Exception):
pass
class RepositoryFilePushError(RepositoryFileError):
pass
class ConfigFileTooLargeError(RepositoryFilePushError):
pass
class QuotaExceeded(RepositoryFilePushError):
pass
class RepositoryFileExistsError(RepositoryFilePushError):
"Attempted to add a file that already exists"
pass
class RepositoryFileVersionMismatchError(RepositoryFilePushError):
"File upload failed because the version changed underneath"
pass
class FileEntryIsDirectory(Exception):
pass
class DirectoryEntryIsFile(Exception):
pass
class UserNotFound(Exception):
pass
class GroupNotFound(Exception):
pass
07070100000026000081B4000000000000000000000001625E850000000EA0000000000000000000000000000000000000002000000000mgr-cfg/config_common/deploy.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 os
import sys
from config_common.transactions import DeployTransaction, FailedRollback
from config_common import file_utils
from config_common import cfg_exceptions
def deploy_msg_callback(path):
print("Deploying %s" % path)
def deploy_files(topdir, repository, files, excludes = None, config_channel = None):
topdir = topdir or os.sep
if not excludes:
excludes = []
dep_trans = DeployTransaction(transaction_root=topdir)
dep_trans.deploy_callback(deploy_msg_callback)
for path in files:
if path in excludes:
print("Excluding %s" % path)
else:
try:
if config_channel:
args = (config_channel, path)
else:
args = (path, )
kwargs = {'auto_delete': 0, 'dest_directory': topdir}
finfo = repository.get_file_info(*args, **kwargs)
except cfg_exceptions.DirectoryEntryIsFile:
e = sys.exc_info()[1]
print("Error: unable to deploy directory %s, as it is already a file on disk" % e[0])
continue
if finfo is None:
# File disappeared since we called the function
continue
(processed_path, file_info, dirs_created) = finfo
try:
dep_trans.add_preprocessed(path, processed_path, file_info, dirs_created)
except cfg_exceptions.UserNotFound:
e = sys.exc_info()[1]
print("Error: unable to deploy file %s, information on user '%s' could not be found." % (path,e[0]))
continue
except cfg_exceptions.GroupNotFound:
e = sys.exc_info()[1]
print("Error: unable to deploy file %s, information on group '%s' could not be found." % (path, e[0]))
continue
try:
dep_trans.deploy()
#5/3/05 wregglej - 136415 added missing user exception stuff.
except cfg_exceptions.UserNotFound:
e = sys.exc_info()[1]
try_rollback(dep_trans, "Error unable to deploy file, information on user '%s' could not be found" % e[0])
except cfg_exceptions.GroupNotFound:
e = sys.exc_info()[1]
try_rollback(dep_trans, "Error: unable to deploy file, information on group '%s' could not be found" % e[0])
except cfg_exceptions.FileEntryIsDirectory:
e = sys.exc_info()[1]
try_rollback(dep_trans, "Error: unable to deploy file %s, as it is already a directory on disk" % e[0])
except cfg_exceptions.DirectoryEntryIsFile:
e = sys.exc_info()[1]
try_rollback(dep_trans, "Error: unable to deploy directory %s, as it is already a file on disk" % e[0])
except Exception:
try:
try_rollback(dep_trans, "Deploy failed, rollback successful")
except:
print("Failed rollback")
raise
def try_rollback(dep_trans, msg):
try:
dep_trans.rollback()
except (FailedRollback,
cfg_exceptions.UserNotFound,
cfg_exceptions.GroupNotFound):
pass
print(msg)
07070100000027000081B4000000000000000000000001625E8500000033FD000000000000000000000000000000000000002400000000mgr-cfg/config_common/file_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 sys
import stat
import time
import tempfile
import base64
import difflib
import pwd
import grp
try:
from selinux import lgetfilecon
except:
# on rhel4 we do not support selinux
def lgetfilecon(path):
return [0, '']
from config_common import utils
from config_common.local_config import get as get_config
from rhn.stringutils import bstr, sstr
decodestring = base64.decodestring
if hasattr(base64, 'decodebytes'):
decodestring = base64.decodebytes
class FileProcessor:
file_struct_fields = {
'file_contents' : None,
'delim_start' : None,
'delim_end' : None,
}
def __init__(self):
pass
def process(self, file_struct, directory=None, strict_ownership=1):
# Older servers will not return directories; if filetype is missing,
# assume file
if file_struct.get('filetype') == 'directory':
if directory is None:
directory = ""
return None, utils.mkdir_p(directory + file_struct['path'])
if directory:
directory += os.path.split(file_struct['path'])[0]
if file_struct.get('filetype') == 'symlink':
if 'symlink' not in file_struct:
raise Exception("Missing key symlink")
(fullpath, dirs_created, fd) = maketemp(prefix=".rhn-cfg-tmp", directory=directory)
os.close(fd)
os.unlink(fullpath)
os.symlink(file_struct['symlink'], fullpath)
return fullpath, dirs_created
for k in self.file_struct_fields.keys():
if k not in file_struct:
# XXX
raise Exception("Missing key %s" % k)
encoding = ''
if 'encoding' in file_struct:
encoding = file_struct['encoding']
contents = file_struct['file_contents']
if contents and (encoding == 'base64'):
contents = decodestring(bstr(contents))
delim_start = file_struct['delim_start']
delim_end = file_struct['delim_end']
if ('checksum' in file_struct
and 'checksum_type' in file_struct
and 'verify_contents' in file_struct
and file_struct['verify_contents']):
if file_struct['checksum'] != utils.getContentChecksum(
file_struct['checksum_type'], contents):
raise Exception("Corrupt file received: Content checksums do not match!")
elif ('md5sum' in file_struct and 'verify_contents' in file_struct
and file_struct['verify_contents']):
if file_struct['md5sum'] != utils.getContentChecksum(
'md5', contents):
raise Exception("Corrupt file received: Content checksums do not match!")
elif ('verify_contents' in file_struct
and file_struct['verify_contents']):
raise Exception("Corrupt file received: missing checksum information!")
(fullpath, dirs_created, fd) = maketemp(prefix=".rhn-cfg-tmp", directory=directory)
try:
os.write(fd, bstr(contents))
except Exception:
raise
finally:
os.close(fd)
# try to set mtime and ctime of the file to
# the last modified time on the server
if 'modified' in file_struct:
try:
modified = xmlrpc_time(file_struct['modified'].value)
epoch_time = time.mktime(modified)
os.utime(fullpath, (epoch_time, epoch_time))
except (ValueError, AttributeError):
# we can't parse modified time
pass
return fullpath, dirs_created
def diff(self, file_struct):
self._validate_struct(file_struct)
temp_file, temp_dirs = self.process(file_struct)
path = file_struct['path']
sectx_result = ''
owner_result = ''
group_result = ''
perm_result = ''
result = ''
stat_err = 0
try:
cur_stat = os.lstat(path)
except:
stat_err = 1
if file_struct['filetype'] != 'symlink':
if not stat_err:
#check for owner differences
cur_uid = cur_stat[stat.ST_UID]
try:
cur_user = pwd.getpwuid(cur_uid)[0]
except KeyError:
#Orphan UID with no name,return unknown
cur_user = "unknown(UID %d)" % (cur_uid,)
else:
cur_user = "missing"
if cur_user == file_struct['username']:
owner_result = ""
else:
owner_result = "User name differ: actual: [%s], expected: [%s]\n" % (cur_user, file_struct['username'])
if not stat_err:
#check for group differences
cur_gid = cur_stat[stat.ST_GID]
try:
cur_group = grp.getgrgid(cur_gid)[0]
except KeyError:
#Orphan GID with no name,return unknown
cur_group = "unknown(GID %d)" % (cur_gid,)
else:
cur_group = "missing"
if cur_group == file_struct['groupname']:
group_result = ""
else:
group_result = "Group name differ: actual: [%s], expected: [%s]\n" % (cur_group, file_struct['groupname'])
#check for permissions differences
if not stat_err:
cur_perm = format(stat.S_IMODE(cur_stat[stat.ST_MODE]), 'o')
else:
cur_perm = "missing"
#rip off the leading '0' from the mode returned by stat()
if cur_perm[0] == '0':
cur_perm = cur_perm[1:]
#perm_status gets displayed with the verbose option.
if cur_perm == str(file_struct['filemode']):
perm_result = ""
else:
perm_result = "File mode differ: actual: [%s], expected: [%s]\n" % (cur_perm, file_struct['filemode'])
try:
cur_sectx = lgetfilecon(path)[1]
except OSError: # workarounding BZ 690238
cur_sectx = None
if cur_sectx == None:
cur_sectx = ''
if 'selinux_ctx' in file_struct and file_struct['selinux_ctx']:
if cur_sectx != file_struct['selinux_ctx']:
sectx_result = "SELinux contexts differ: actual: [%s], expected: [%s]\n" % (cur_sectx, file_struct['selinux_ctx'])
if file_struct['filetype'] == 'directory':
if os.path.isdir(file_struct['path']):
result = ''
else:
result = "Deployed directory is no longer a directory!"
elif file_struct['filetype'] == 'symlink':
try:
curlink = os.readlink(path)
newlink = os.readlink(temp_file)
if curlink == newlink:
result = ''
else:
result = "Link targets differ for [%s]: actual: [%s], expected: [%s]\n" % (path, curlink, newlink)
except OSError:
e = sys.exc_info()[1]
if e.errno == 22:
result = "Deployed symlink is no longer a symlink!"
else:
raise e
else:
result = ''.join(diff(temp_file, path, display_diff=get_config('display_diff'),
is_binary=False if file_struct['is_binary'] == 'N' else True))
if temp_file:
os.unlink(temp_file)
return owner_result + group_result + perm_result + sectx_result + result
def _validate_struct(self, file_struct):
for k in self.file_struct_fields.keys():
if k not in file_struct:
# XXX
raise Exception("Missing key %s" % k)
def diff(src, dst, srcname=None, dstname=None, display_diff=False, is_binary=False):
def f_content(path, name, is_binary):
statinfo = None
if os.access(path, os.R_OK):
f = open(path, ('r' if int(sys.version[0]) == 3 else 'U') + ('b' if is_binary else ''))
content = [sstr(i) for i in f.readlines()]
f.close()
statinfo = os.stat(path)
f_time = time.ctime(statinfo.st_mtime)
if not is_binary and content and content[-1] and content[-1][-1] != "\n":
content[-1] += "\n"
else:
content = []
f_time = time.ctime(0)
if not name:
name = path
if is_binary:
return (str(content), name, f_time, statinfo)
return (content, name, f_time, statinfo)
(src_content, src_name, src_time, src_stat) = f_content(src, srcname, is_binary)
(dst_content, dst_name, dst_time, dst_stat) = f_content(dst, dstname, is_binary)
diff_u = difflib.unified_diff(src_content, dst_content,
src_name, dst_name,
src_time, dst_time)
ret_list = list(diff_u)
# don't return the diff if the file is not readable by everyone
# for security reasons.
if (len(ret_list) > 0 # if differences exist
and not display_diff # and we have not explicitly decided to display
and (dst_stat == None # file is not there or not readable to root
or (dst_stat.st_uid == 0 # file is owned by root
and not dst_stat.st_mode & stat.S_IROTH))): # not read-all
ret_list = [
"Differences exist in a file %s that is not readable by all. " % dst,
"Re-deployment of configuration file is recommended."]
return ret_list
def maketemp(prefix=None, directory=None):
"""Creates a temporary file (guaranteed to be new), using the
specified prefix.
Returns the filename and a file descriptor
"""
if not directory:
directory = tempfile.gettempdir()
dirs_created = None
if not os.path.exists(directory):
dirs_created = utils.mkdir_p(directory)
if not prefix:
# Create the file in /tmp by default
prefix = 'rhncfg-tempfile'
file_prefix = "%s-%s-" % (prefix, os.getpid())
(fd, filename) = tempfile.mkstemp(prefix=file_prefix, dir=directory)
return filename, dirs_created, fd
# Duplicated from backend/common/fileutils.py to remove dependency requirement.
# If making changes make them there too.
FILETYPE2CHAR = {
'file' : '-',
'directory' : 'd',
'symlink' : 'l',
'chardev' : 'c',
'blockdev' : 'b',
}
# Duplicated from backend/common/fileutils.py to remove dependency requirement.
# If making changes make them there too.
def _ifelse(cond, thenval, elseval):
if cond:
return thenval
else:
return elseval
# Duplicated from backend/common/fileutils.py to remove dependency requirement.
# If making changes make them there too.
def ostr_to_sym(octstr, ftype):
""" Convert filemode in octets (like '644') to string like "ls -l" ("-rwxrw-rw-")
ftype is one of: file, directory, symlink, chardev, blockdev.
"""
mode = int(str(octstr), 8)
symstr = FILETYPE2CHAR.get(ftype, '?')
symstr += _ifelse(mode & stat.S_IRUSR, 'r', '-')
symstr += _ifelse(mode & stat.S_IWUSR, 'w', '-')
symstr += _ifelse(mode & stat.S_IXUSR,
_ifelse(mode & stat.S_ISUID, 's', 'x'),
_ifelse(mode & stat.S_ISUID, 'S', '-'))
symstr += _ifelse(mode & stat.S_IRGRP, 'r', '-')
symstr += _ifelse(mode & stat.S_IWGRP, 'w', '-')
symstr += _ifelse(mode & stat.S_IXGRP,
_ifelse(mode & stat.S_ISGID, 's', 'x'),
_ifelse(mode & stat.S_ISGID, 'S', '-'))
symstr += _ifelse(mode & stat.S_IROTH, 'r', '-')
symstr += _ifelse(mode & stat.S_IWOTH, 'w', '-')
symstr += _ifelse(mode & stat.S_IXOTH,
_ifelse(mode & stat.S_ISVTX, 't', 'x'),
_ifelse(mode & stat.S_ISVTX, 'T', '-'))
return symstr
# Duplicated from backend/common/fileutils.py to remove dependency requirement.
# If making changes make them there too.
def f_date(dbiDate):
return "%04d-%02d-%02d %02d:%02d:%02d" % (dbiDate.year, dbiDate.month,
dbiDate.day, dbiDate.hour, dbiDate.minute, dbiDate.second)
def xmlrpc_time(xtime):
if xtime[8] == 'T':
# oracle backend: 20130304T23:19:17
timefmt='%Y%m%dT%H:%M:%S'
else:
# postresql backend format: 2014-02-28 18:47:31.506953+01:00
timefmt='%Y-%m-%d %H:%M:%S'
xtime = xtime[:19]
return time.strptime(xtime, timefmt)
07070100000028000081B4000000000000000000000001625E850000000C44000000000000000000000000000000000000002600000000mgr-cfg/config_common/handler_base.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 getpass
from optparse import OptionParser, Option
from config_common import rhn_log
from config_common import cfg_exceptions
from config_common import local_config
from rhn.stringutils import bstr
class HandlerBase:
_options_table = []
_option_parser_class = OptionParser
_usage_options = "[options]"
_option_class = Option
def __init__(self, args, repository, mode=None, exec_name=None):
self.repository = repository
self.set_mode(mode)
self.set_exec_name(exec_name)
self.options, self.args = self._parse_args(args)
def set_mode(self, mode):
self.mode = mode
def set_exec_name(self, exec_name):
self.exec_name = exec_name
def _prog(self):
return "%s %s" % (sys.argv[0], self.mode or "<unknown>")
def _parse_args(self, args):
# Parses the arguments and returns a tuple (options, args)
usage = " ".join(["%prog", self.mode, self._usage_options])
self._parser = self._option_parser_class(
option_list=self._options_table,
usage=usage)
return self._parser.parse_args(args)
def usage(self):
return self._parser.print_help()
def authenticate(self, username=None, password=None):
# entry point for repository authentication
try:
self.repository.login()
except cfg_exceptions.InvalidSession:
if not username :
username=local_config.get('username')
if not password :
password=local_config.get('password')
if not password :
(username, password) = self.get_auth_info(username)
try:
self.repository.login(username=username, password=password)
except cfg_exceptions.InvalidSession:
e = sys.exc_info()[1]
rhn_log.die(1, "Session error: %s\n" % e)
def get_auth_info(self, username=None):
if username is None:
username = self._read_username()
password = getpass.getpass()
return (username, password)
def _read_username(self):
tty = open("/dev/tty", "rb+", buffering=0)
tty.write(bstr("SUSE Manager username: "))
try:
username = tty.readline()
except KeyboardInterrupt:
tty.write(bstr("\n"))
sys.exit(0)
if username is None:
# EOF
tty.write(bstr("\n"))
sys.exit(0)
return username.strip()
07070100000029000081B4000000000000000000000001625E85000000117C000000000000000000000000000000000000002600000000mgr-cfg/config_common/local_config.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 os
import sys
# python2
try:
import ConfigParser
except: #python3
import configparser as ConfigParser
from config_common import utils
class rhncfgConfigParser(ConfigParser.ConfigParser):
_local_config_file_name = '.rhncfgrc'
_instance = None
def __init__(self, section, defaults=None, config_file_override=None):
"""defaults is either None, or a dictionary of default values which can be overridden"""
if defaults:
for (k, v) in defaults.items():
if type(v) == int:
defaults[k] = str(v)
ConfigParser.ConfigParser.__init__(self, defaults)
self.section = section
self.overrides = {}
self.mydefaults = self.defaults()
self.config_file_override = config_file_override
def read_config_files(self, overrides={}):
self.overrides.clear()
self.overrides.update(overrides)
try:
self.read(self._get_config_files())
except ConfigParser.MissingSectionHeaderError:
e = sys.exc_info()[1]
print("Config error: line %s, file %s: %s" % (e.lineno,
e.filename, e))
sys.exit(1)
def _get_config_files(self):
if sys.platform.find('sunos') > -1:
default_config_file = "/opt/redhat/rhn/solaris/etc/sysconfig/rhn/%s.conf" % self.section
else:
default_config_file = "/etc/sysconfig/rhn/%s.conf" % self.section
config_file_list = [
default_config_file,
os.path.join(utils.get_home_dir(), self._local_config_file_name),
self._local_config_file_name,
]
if self.config_file_override:
config_file_list.append(self.config_file_override)
return config_file_list
def get_option(self, option):
#6/29/05 wregglej 152388
# server_list is always in the defaults, never in the rhncfg config file. It's formed when there
# are more than one server in up2date's serverURL setting.
if option == 'server_list':
if 'server_list' in self.mydefaults:
if type(self.mydefaults['server_list']) is type([]):
return self.mydefaults['server_list']
try:
ret = self.get(self.section, option, vars=self.overrides)
#5/25/05 wregglej - 158694
#Move the cast to an int to here from the up2date_config_parser, that way the stuff that needs to get interpolated
#gets interpolated, the stuff that should be an int ends up and int, and the stuff that's neither doesn't get
#messed with.
try:
if type(ret) != type([]):
ret = int(ret)
except ValueError:
pass
return ret
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
pass
defaults = self.defaults()
return defaults.get(option)
def keys(self):
return self.options(self.section)
def __getitem__(self, item):
return self.get_option(item)
def init(section, defaults=None, config_file_override=None, **overrides):
cp = rhncfgConfigParser._instance = rhncfgConfigParser(section, defaults, config_file_override=config_file_override)
cp.read_config_files(overrides)
def get(var):
return _get_config().get_option(var)
def _get_config():
if rhncfgConfigParser._instance is None:
raise ValueError("Configuration not initialized")
return rhncfgConfigParser._instance
def instance():
return _get_config()
def keys():
return list(_get_config().keys())
def main():
init('rhncfgcli')
print("repository: %s" % get("repository"))
print("useGPG: %s" % get("useGPG"))
print("serverURL: %s" % get("serverURL"))
if __name__ == '__main__':
main()
0707010000002A000081B4000000000000000000000001625E850000003D3A000000000000000000000000000000000000002400000000mgr-cfg/config_common/repository.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 re
import os
import pwd
import grp
import sys
import stat
import base64
from config_common import cfg_exceptions
from config_common import local_config
from config_common import utils
from config_common.rhn_log import log_debug
from rhn import rpclib
Output = rpclib.transports.Output
try: # python2
PY3 = False
import xmlrpclib
except ImportError: # python3
PY3 = True
import xmlrpc.client as xmlrpclib
basestring = (str, bytes)
from uyuni.common.usix import raise_with_tb
try:
from selinux import lgetfilecon
except:
# on rhel4 we do not support selinux
def lgetfilecon(path):
return [0, '']
#6/29/05 rpc_wrapper implements the failover logic.
from config_common import rpc_wrapper
rpclib = rpc_wrapper
def deci_to_octal(number):
"""convert a normal decimal int to another int representing the octal value"""
# python 2.4/2.6: oct(420) -> '0644'
# python 3.4: oct(420) -> '0o644'
return int(oct(number).replace("o", ""))
class Repository:
_uid_cache = {}
_gid_cache = {}
_local_config = local_config
def __init__(self):
self.default_delimiters = None
self.maximum_file_size = None
# Helpers
# Unless overridden in a subclass, per-file delimiters are the same as the
# global delimiters
def get_file_delimiters(self, file):
"returns the default delimiters for this file"
return self.get_default_delimiters()
def get_default_delimiters(self):
"returns the default delimiters"
if self.default_delimiters is None:
self.default_delimiters = self._get_default_delimiters()
return self.default_delimiters
def _get_default_delimiters(self):
raise NotImplementedError
def get_maximum_file_size(self):
"returns the maximum file size"
if self.maximum_file_size is None:
self.maximum_file_size = self._get_maximum_file_size()
return self.maximum_file_size
def _get_maximum_file_size(self):
"To be overwritten in subclasses"
return 1024
def make_stat_info(self, path, file_stat):
# Returns the stat information as required by the API
ret = {}
fields = {
'mode' : stat.ST_MODE,
'user' : stat.ST_UID,
'group' : stat.ST_GID,
'size' : stat.ST_SIZE,
'mtime' : stat.ST_MTIME,
'ctime' : stat.ST_CTIME,
}
for label, st in fields.items():
ret[label] = file_stat[st]
# server expects things like 644, 700, etc.
ret['mode'] = deci_to_octal(ret['mode'] & int('07777', 8))
#print ret['size']
#if ret['size'] > self.get_maximum_file_size():
# die(4, "File %s exceeds the maximum file size (%s)" %
# (path, ret['size']))
uid = ret['user']
gid = ret['group']
pw_name = self._uid_cache.get(uid)
if not pw_name:
try:
pw_name = pwd.getpwuid(uid)[0]
except KeyError:
print("Error looking up user id %s" % (uid, ))
if pw_name:
ret['user'] = pw_name
self._uid_cache[uid] = pw_name
gr_name = self._gid_cache.get(gid)
if not gr_name:
try:
gr_name = grp.getgrgid(gid)[0]
except KeyError:
print("Error looking up group id %s" % (gid, ))
if gr_name:
ret['group'] = gr_name
self._gid_cache[gid] = gr_name
# if selinux is disabled or on RHEL4 we do not send the selinux_ctx
# flag at all - see bug 644985 - SELinux context cleared from
# RHEL4 rhncfg-client
try:
selinux_ctx = lgetfilecon(path)[1]
except OSError:
selinux_ctx = ''
if selinux_ctx == None:
selinux_ctx = ''
ret['selinux_ctx'] = selinux_ctx
return ret
def _make_file_info(self, remote_path, local_path=None, delim_start=None,
delim_end=None, load_contents=1):
if not local_path:
# Safe enough to assume local path is the same as the remote one
local_path = remote_path
try:
file_stat = os.lstat(local_path)
except OSError:
e = sys.exc_info()[1]
raise_with_tb(cfg_exceptions.RepositoryLocalFileError(
"Error lstat()-ing local file: %s" % e), sys.exc_info()[2])
# Dlimiters
if delim_start or delim_end:
if not (delim_start and delim_end):
# If only one delimiter is provided, assume the delimiters are
# the same, whatever that is (or is nice)
delim_start = delim_end = (delim_start or delim_end)
else:
# Use the default
delim_start, delim_end = self.get_file_delimiters(remote_path)
params = {
'path' : remote_path,
'delim_start' : delim_start,
'delim_end' : delim_end,
}
file_contents = None
if os.path.islink(local_path):
params['config_file_type_id'] = 3
params['symlink'] = os.readlink(local_path)
load_contents = 0
elif os.path.isdir(local_path):
params['config_file_type_id'] = 2
load_contents = 0
else:
params['config_file_type_id'] = 1
if load_contents:
try:
file_contents = open(local_path, "rb").read()
except IOError:
e = sys.exc_info()[1]
raise_with_tb(cfg_exceptions.RepositoryLocalFileError(
"Error opening local file: %s" % e), sys.exc_info()[2])
self._add_content(file_contents, params)
params.update(self.make_stat_info(local_path, file_stat))
return params
def _add_content(self, file_contents, params):
"""Add the file contents to the params hash"""
params['enc64'] = 1
params['file_contents'] = base64.encodestring(file_contents)
def login(self, username=None, password=None):
pass
class RPC_Repository(Repository):
def __init__(self, setup_network=1):
Repository.__init__(self)
# all this so needs to be in a seperate rhnConfig library,
# shared by up2date, rhncfg*, etc.
#
# But, I digress.
self.__server_url = self._local_config.get('server_url')
# 6/29/05 wregglej 152388
# server_list contains the list of servers to failover to.
self.__server_list = self._local_config.get('server_list')
# 6/29/05 wregglej 152388
# Grab server_handler, which is different for rhncfg-client and rhncfg-manager
# and is needed when failover occurs. During a failover, when the server object is
# being set up to use a new satellite, the server_handler is added to the address so
# the tool communicates with the correct xmlrpc handler.
handler = self._local_config.get('server_handler')
cap_handler = re.sub('[^/]+$', 'XMLRPC', handler)
if not self.__server_url:
raise cfg_exceptions.ConfigurationError(
"Missing entry 'server_url' in the config files\n" \
"Try running as root, or configure server_url as described in the configuration file"
)
log_debug(3, "server url", self.__server_url)
self.__proxy_user = None
self.__proxy_password = None
self.__proxy_host = None
self.__enable_proxy = self._local_config.get('enableProxy')
self.__enable_proxy_auth = self._local_config.get('enableProxyAuth')
if self.__enable_proxy:
self.__proxy_host = self._local_config.get('httpProxy')
if self.__enable_proxy_auth:
self.__proxy_user = self._local_config.get('proxyUser')
self.__proxy_password = self._local_config.get('proxyPassword')
ca = self._local_config.get('sslCACert')
if isinstance(ca, basestring):
ca = [ca]
ca_certs = ca or ["/usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT"]
# not sure if we need this or not...
lang = None
for env in 'LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG':
if env in os.environ:
if not os.environ[env]:
# sometimes unset
continue
lang = os.environ[env].split(':')[0]
lang = lang.split('.')[0]
break
if setup_network:
# Fetch server capabilities - we need the /XMLRPC handler
#t = list(utils.parse_url(self.__server_url))
#t[2] = '/XMLRPC'
#x_server_url = utils.unparse_url(t)
# 6/29/05 wregglej 152388
# Fetching the server capabilities involves using the /XMLRPC handler. It's
# the only place that I know of that does that. The server_url and server_list
# both need to have /XMLRPC on the ends, which is what _patch_uris() does by default.
x_server_url = self._patch_uris(self.__server_url, cap_handler)
if self.__server_list != None:
x_server_list = self._patch_uris(self.__server_list, cap_handler)
else:
x_server_list = None
x_server = rpclib.Server(x_server_url,
proxy=self.__proxy_host,
username=self.__proxy_user,
password=self.__proxy_password,
server_list=x_server_list,
rpc_handler="/XMLRPC")
# Make a call to a function that can export the server's capabilities
# without setting any state on the server side
try:
x_server.registration.welcome_message()
except xmlrpclib.Fault:
e = sys.exc_info()[1]
sys.stderr.write("XML-RPC error while talking to %s:\n %s\n" % (self.__server_url, e))
sys.exit(2)
self._server_capabilities = get_server_capability(x_server)
del x_server
# 6/29/05 wregglej 152388
# From here on out all communication should take place through the xmlrpc handler
# that's appropriate for the tool being used. For rhncfg-client that's /CONFIG-MANAGEMENT.
# For rhncfg-manager that's /CONFIG-MANAGEMENT-TOOL. No, I don't know the reasoning behind that.
# First we need to patch the uris in server_list, to use the correct handler.
self.__server_url = self._patch_uris(self.__server_url, handler)
if self.__server_list != None:
self.__server_list = self._patch_uris(self.__server_list, handler)
else:
self.__server_list = None
self.server = rpclib.Server(self.__server_url,
proxy=self.__proxy_host,
username=self.__proxy_user,
password=self.__proxy_password,
server_list=self.__server_list,
rpc_handler=handler)
self._set_capabilities()
self.server.set_transport_flags(
transfer=Output.TRANSFER_BINARY,
encoding=Output.ENCODE_GZIP
)
if lang:
self.server.setlang(lang)
for ca_cert in ca_certs:
if not os.access(ca_cert, os.R_OK):
raise cfg_exceptions.ConfigurationError("Can not find CA file: %s" % ca_cert)
log_debug(3, "ca cert", ca_cert)
# force the validation of the SSL cert
self.server.add_trusted_cert(ca_cert)
# 6/29/05 wregglej 152388
# Places handler at the end of the uri.
# uris can be either a uri string or a list of uri strings.
def _patch_uris(self, uris, handler="/XMLRPC"):
#Handles patching the uris when they're in a list.
if type(uris) == type([]):
ret = []
for i in range(len(uris)):
t = list(utils.parse_url(uris[i]))
t[2] = handler
ret.append(utils.unparse_url(t))
#Handles patching the uri when it's a string.
else:
t = list(utils.parse_url(uris))
t[2] = handler
ret = utils.unparse_url(t)
return ret
def _set_capabilities(self):
# list of client capabilities
capabilities = {
'configfiles.base64_enc' : {'version' : 1, 'value' : 1},
'rhncfg.dirs_enabled' : {'version' : 1, 'value' : 1},
}
for name, hashval in capabilities.items():
cap = "%s(%s)=%s" % (name, hashval['version'], hashval['value'])
self.server.add_header("X-RHN-Client-Capability", cap)
def rpc_call(self, method_name, *params):
method = getattr(self.server, method_name)
try:
result = method(*params)
except xmlrpclib.ProtocolError:
e = sys.exc_info()[1]
sys.stderr.write("XML-RPC call error: %s\n" % e)
sys.exit(1)
except xmlrpclib.Fault:
# Re-raise them
raise
except Exception:
e = sys.exc_info()[1]
sys.stderr.write("XML-RPC error while talking to %s: %s\n" % (
self.__server_url, e))
sys.exit(2)
return result
def _get_maximum_file_size(self):
return self.rpc_call('config.max_upload_fsize')
def _add_content(self, file_contents, params):
"""Add the file contents to the params hash"""
# check for the rhncfg.content.base64_decode capability and encode the
# data if the server is capable of descoding it
if 'rhncfg.content.base64_decode' in self._server_capabilities:
params['enc64'] = 1
params['file_contents'] = base64.encodestring(file_contents)
else:
params['file_contents'] = file_contents
return params
def get_server_capability(s):
headers = s.get_response_headers()
if headers is None:
# No request done yet
return {}
if PY3:
cap_headers = ["X-RHN-Server-Capability: %s" % val for val in headers.get_all("X-RHN-Server-Capability")]
else:
cap_headers = headers.getallmatchingheaders("X-RHN-Server-Capability")
if not cap_headers:
return {}
regexp = re.compile(
r"^(?P<name>[^(]*)\((?P<version>[^)]*)\)\s*=\s*(?P<value>.*)$")
vals = {}
for h in cap_headers:
arr = h.split(':', 1)
assert len(arr) == 2
val = arr[1].strip()
if not val:
continue
mo = regexp.match(val)
if not mo:
# XXX Just ignoring it, for now
continue
vdict = mo.groupdict()
for k, v in vdict.items():
vdict[k] = v.strip()
vals[vdict['name']] = vdict
return vals
0707010000002B000081B4000000000000000000000001625E850000000EC9000000000000000000000000000000000000002100000000mgr-cfg/config_common/rhn_log.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 os
import stat
import sys
import time
import traceback
class Logger:
debug_level = 1
logfile = None
def log_debug(self, debug_level, *args):
if debug_level <= self.debug_level:
outstr = "%s %s: %s\n" % (
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())),
self.get_caller(),
" ".join([str(it) for it in args]))
sys.stdout.write(outstr)
if not self.logfile is None:
self.write_to_logfile(outstr)
def log_to_file(self, debug_level, *args):
if debug_level <= self.debug_level:
outstr = "%s %s: %s\n" % (
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())),
self.get_caller(),
" ".join([str(it) for it in args]))
if not self.logfile is None:
self.write_to_logfile(outstr)
def write_to_logfile(self, logstr):
if os.access(self.logfile, os.F_OK|os.R_OK|os.W_OK):
logname = open(self.logfile, "a")
logname.write(logstr)
logname.close()
else:
#pkilambi: bug#179367: check permissions before writing.
#non-root users will not have permissions to create the file
# Set to root-RW-only if we have to create the file
mode = stat.S_IRUSR | stat.S_IWUSR # octal 0o600
try:
# Need to use os.open to insure initial permissions
fd = os.open(self.logfile, os.O_WRONLY | os.O_CREAT, mode)
logname = os.fdopen(fd, 'w')
logname.write(logstr)
logname.close()
except:
print("does not have permissions to create file %s" % (self.logfile))
sys.exit(1)
def set_logfile(self, filename):
Logger.logfile = filename
def set_debug_level(self, debug_level):
Logger.debug_level = debug_level
def get_debug_level(self):
return Logger.debug_level
def get_caller(self, caller_offset=4):
tbStack = traceback.extract_stack()
callid = len(tbStack) - caller_offset
module = tbStack[callid]
module_file = os.path.basename(module[0])
module_file = module_file.split('.', 1)[0]
return "%s.%s" % (module_file, module[2])
def log_error(self, *args):
outstr = " ".join([str(it) for it in args])
sys.stderr.write(outstr)
sys.stderr.write("\n")
if not self.logfile is None:
self.write_to_logfile(outstr)
def die(self, error_code, *args):
self.log_error(args)
sys.exit(error_code)
def set_debug_level(*args):
return Logger().set_debug_level(*args)
def get_debug_level(*args):
return Logger().get_debug_level(*args)
def set_logfile(*args):
return Logger().set_logfile(*args)
def log_debug(*args):
return Logger().log_debug(*args)
def log_to_file(*args):
return Logger().log_to_file(*args)
def log_error(*args):
return Logger().log_error(*args)
def die(error_code, *args):
Logger().log_error(*args)
sys.exit(error_code)
0707010000002C000081B4000000000000000000000001625E85000000208E000000000000000000000000000000000000002200000000mgr-cfg/config_common/rhn_main.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 os
import sys
from config_common import local_config
from config_common import rhn_log
from config_common import utils
from config_common import cfg_exceptions
try:
# python3
from urllib.parse import urlsplit
from configparser import InterpolationError
except ImportError: #python2
from urlparse import urlsplit
from ConfigParser import InterpolationError
try:
from socket import gaierror
except:
from socket import error
gaierror = error
from up2date_client import config
class BaseMain:
modes = []
repository_class_name = "Repository"
plugins_dir = 'config_common'
mode_prefix = None
config_section = None
def usage(self, exit_code):
print("Usage: %s MODE [ --config config_file ] [ --server-name name ] [ params ]" % sys.argv[0])
print("Valid modes are:")
for mode in self.modes:
print("\t%s" % mode)
sys.exit(exit_code)
def main(self):
args = []
show_help = None
debug_level = 3
mode = None
dict_name_opt={'--server-name': None,'--password': None,'--username': None, '--config': None,}
for index in range(1,len(sys.argv)):
arg=sys.argv[index]
param = [x for x in dict_name_opt.items() if x[1] == 0]
if param:
if arg.startswith('-') or arg in self.modes:
# not perfect, but at least a little bit better
print("Option %s requires an argument" % dict_name_opt[param[0][0]])
return 1
dict_name_opt[param[0][0]] = arg
continue
if arg in ('--help', '-h'):
show_help = 1
continue
param = [s for s in dict_name_opt.keys() if arg.startswith(s)]
if param:
rarg = arg[len(param[0]):]
if not rarg:
dict_name_opt[param[0]] = 0
if index == len(sys.argv) - 1:
print("Option %s requires an argument" % param[0])
return 1
continue
if rarg[0] == '=':
if len(rarg) == 1:
print("Option %s requires an argument" % param[0])
return 1
dict_name_opt[param[0]] = rarg[1:]
continue
print("Unknown option %s" % arg)
return 1
if mode is None:
# This should be the mode
mode = arg
if mode == '':
# Bad
self.usage(1)
if mode[0] == '-':
# Mode can't be an option
self.usage(1)
if mode not in self.modes:
print("Unknown mode %s" % mode)
self.usage(1)
continue
args.append(arg)
server_name = dict_name_opt['--server-name']
password = dict_name_opt['--password']
username = dict_name_opt['--username']
config_file_override = dict_name_opt['--config']
if config_file_override and not os.path.isfile(config_file_override):
rhn_log.die(1, "Config file %s does not exist.", config_file_override)
rhn_log.set_debug_level(debug_level)
if mode is None:
# No args were specified
self.usage(0)
execname = os.path.basename(sys.argv[0])
# Class names cannot have dot in them, so strip the extension
execname = execname.split('.', 1)[0]
mode_module = mode.replace('-', '_')
module_name = "%s_%s" % (self.mode_prefix, mode_module)
full_module_name = "%s.%s" % (self.plugins_dir, module_name)
try:
module = __import__(full_module_name)
except ImportError:
e = sys.exc_info()[1]
rhn_log.die(1, "Unable to load plugin for mode '%s': %s" % (mode, e))
module = getattr(module, module_name)
if show_help:
# Display the per-module help
handler = module.Handler(args, None, mode=mode, exec_name=execname)
handler.usage()
return 0
cfg = config.initUp2dateConfig()
up2date_cfg = dict(cfg.items())
if server_name:
local_config.init(self.config_section, defaults=up2date_cfg, config_file_override=config_file_override, server_url="https://" + server_name)
else:
local_config.init(self.config_section, defaults=up2date_cfg, config_file_override=config_file_override)
try:
server_name = local_config.get('server_url')
except InterpolationError:
e = sys.exc_info()[1]
if e.option == 'server_url':
server_name = config.getServerlURL()
up2date_cfg['proto'] = urlsplit(server_name[0])[0]
if up2date_cfg['proto'] == '':
up2date_cfg['proto'] = 'https'
up2date_cfg['server_list'] = [urlsplit(x)[2] for x in server_name]
else:
up2date_cfg['server_list'] = [urlsplit(x)[1] for x in server_name]
server_name = (up2date_cfg['server_list'])[0]
local_config.init(self.config_section, defaults=up2date_cfg, config_file_override=config_file_override, server_name=server_name)
print("Using server name %s" % server_name)
# set the debug level through the config
rhn_log.set_debug_level(int(local_config.get('debug_level') or 0))
rhn_log.set_logfile(local_config.get('logfile') or "/var/log/rhncfg")
# Multi-level import - __import__("foo.bar") does not return the bar
# module, but the foo module with bar loaded
# XXX Error checking
repo_class = local_config.get('repository_type')
if repo_class is None:
rhn_log.die(1, "repository_type not set (missing configuration file?)")
repo_module_name = "%s.%s" % (self.plugins_dir, repo_class)
try:
repo_module = __import__(repo_module_name)
except ImportError:
e = sys.exc_info()[1]
rhn_log.die(1, "Unable to load repository module: %s" % e)
try:
repo_module = getattr(repo_module, repo_class)
except AttributeError:
rhn_log.die(1, "Malformed repository module")
try:
repo = getattr(repo_module, self.repository_class_name)()
except AttributeError:
rhn_log.die(1, "Missing repository class")
except InterpolationError:
e = sys.exc_info()[1]
if e.option == 'server_url':
#pkilambi: bug#179367# backtic is replaced with single quote
rhn_log.die(1, "Missing 'server_url' configuration variable; please refer to the config file")
raise
except cfg_exceptions.ConfigurationError:
e = sys.exc_info()[1]
rhn_log.die(e)
except gaierror:
e = sys.exc_info()[1]
print("Socket Error: %s" % (e.args[1],))
sys.exit(1)
handler = module.Handler(args, repo, mode=mode, exec_name=execname)
try:
try:
handler.authenticate(username, password)
handler.run()
except cfg_exceptions.AuthenticationError:
e = sys.exc_info()[1]
rhn_log.die(1, "Authentication failed: %s" % e)
except Exception:
raise
finally:
repo.cleanup()
return 0
0707010000002D000081B4000000000000000000000001625E850000002318000000000000000000000000000000000000002500000000mgr-cfg/config_common/rpc_wrapper.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
from rhn import rpclib
from uyuni.common.usix import raise_with_tb
try:
from socket import error, sslerror, herror, gaierror, timeout
except ImportError:
from socket import error
sslerror = error
herror = error
gaierror = error
timeout = error
try: # python2
import xmlrpclib
import urllib
except ImportError: # python3
import xmlrpc.client as xmlrpclib
import urllib.parse as urllib
#This is raised when the failover stuff has gone through every server in the server list
#and the error is still occurring.
class NoMoreServers(Exception):
pass
#This was supposed to be a wrapper that contained a reference to a rpclib.Server object and delegated calls to it,
#but it turns out that I needed to override _request() to make sure that all
#communication errors are caught and handled here, so I changed it to be a subclass of rpclib.Server.
#The problem that spurred that was when a xmlrpc function was called on an object that was an
#attribute of the server class it wasn't passing through the _request that I had written here,
#it was going directly to rpclib.Server's _request() and missing all of the failover logic that I had added.
#The short version is that I needed to make sure this class was in the inheritance hierarchy.
class Server(rpclib.Server):
def __init__(self, uri, transport=None, encoding=None, verbose=0,
proxy=None, username=None, password=None, refreshCallback=None,
progressCallback=None, server_list=None, rpc_handler=None):
self.list_of_uris = None #Contains all of the possible uris.
self.current_index = 0 #index of the uri that we're currently using.
#If server_list is None, then no failover systems were listed in the up2date config and
#we need to use the one that was put together in the rhncfg-* config, which was passed in as
#uri.
if server_list is None:
if type(uri) == type([]):
self.list_of_uris = uri
else:
self.list_of_uris = [uri]
else:
#If the server_url passed in is the same as the first element of the server_list, then
#that means all of the server info came from the up2date config (or is the same as the up2date config)
#and we should use the server_list.
#If they don't match then we should use the server_url passed in as uri, because it's a specific setting from
#the rhncfg-*.conf file.
if uri == server_list[0]:
self.list_of_uris = server_list
else:
self.list_of_uris = [uri]
self.rpc_handler = rpc_handler
#Grabs the initial uri that we're going to use.
init_uri = self._get_uri()
#self.rpc_args = {
# 'transport' : transport,
# 'encoding' : encoding,
# 'verbose' : verbose,
# 'proxy' : proxy,
# 'username' : username,
# 'password' : password,
# 'refreshCallback' : refreshCallback,
# 'progressCallback' : progressCallback,
# }
#Set up the rpclib.Server stuff with the first uri.
rpclib.Server.__init__(self, init_uri, transport=transport, encoding=encoding, verbose=verbose,\
proxy=proxy, username=username, password=password, refreshCallback=refreshCallback,\
progressCallback=progressCallback)
#Return the uri that we should be using.
def _get_uri(self):
return self.list_of_uris[self.current_index]
#Returns the list of uris that could be used.
def get_uri_list(self):
return self.list_of_uris
#This is called when we failover. It re-inits the server object to use the new uri. Most of this was cribbed from
#alikins' wrapper that does a similar thing for up2date.
def init_server(self, myuri):
#Borrowed the following from rpcServer.py
#rpclib.Server.__init__(self, uri, transport=self.rpc_args['transport'], encoding=self.rpc_args['encoding'], verbose=self.rpc_args['verbose'],\
# proxy=self.rpc_args['proxy'], username=self.rpc_args['username'],\
# password=self.rpc_args['password'], refreshCallback=self.rpc_args['refreshCallback'],\
# progressCallback=self.rpc_args['progressCallback'])
self._uri = myuri
typ, uri = urllib.splittype(self._uri)
typ = typ.lower()
if typ not in ("http", "https"):
raise InvalidRedirectionError(
"Redirected to unsupported protocol %s" % typ)
self._host, self._handler = urllib.splithost(uri)
self._orig_handler = self._handler
self._type = typ
if not self._handler:
self._handler = self.rpc_handler
self._allow_redirect = 1
del self._transport
self._transport = self.default_transport(typ, self._proxy,
self._username, self._password)
self.set_progress_callback(self._progressCallback)
self.set_refresh_callback(self._refreshCallback)
self.set_buffer_size(self._bufferSize)
self.setlang(self._lang)
if self._trusted_cert_files != [] and \
hasattr(self._transport, "add_trusted_cert"):
for certfile in self._trusted_cert_files:
self._transport.add_trusted_cert(certfile)
#This is the logic for switching to a new server resides.
def _failover(self):
#The print statements are from alikins rpcServer.py.
msg = "An error occurred talking to %s:\n" % self._get_uri()
msg = msg + "%s\n%s\n" % (sys.exc_info()[0], sys.exc_info()[1])
#Increments the index to point to the next server in self.list_of_uris
self.current_index = self.current_index + 1
#Make sure we don't try to go past the end of the list.
if self.current_index > (len(self.list_of_uris) - 1):
raise NoMoreServers()
else:
failover_uri = self._get_uri() #Grab the uri of the new server to use.
msg = msg + "Trying the next serverURL: %s\n" % failover_uri
print(msg)
#Set up rpclib.Server to use the new uri.
self.init_server(failover_uri)
#This is where the magic happens. function is a function reference, arglist is the list of arguements
#that get passed to the function and kwargs is the list of named arguments that get passed into the function.
#I used apply() here so it will work with Python 1.5, which doesn't have the extended call syntax.
def _call_function(self, function, arglist, kwargs={}):
succeed = 0
while succeed == 0:
try:
ret = function(*arglist, **kwargs)
except rpclib.InvalidRedirectionError:
raise
except xmlrpclib.Fault:
e = sys.exc_info()[1]
save_traceback = sys.exc_info()[2]
try:
self._failover()
except NoMoreServers:
f = sys.exc_info()[1]
raise_with_tb(e, save_traceback) #Don't raise the NoMoreServers error, raise the error that triggered the failover.
continue
except (error, sslerror, herror, gaierror, timeout):
e = sys.exc_info()[1]
save_traceback = sys.exc_info()[2]
try:
self._failover()
except NoMoreServers:
raise_with_tb(e, save_traceback)
continue
succeed = 1 #If we get here then the function call eventually succeeded and we don't need to try again.
return ret
def _request(self, methodname, params):
return self._call_function(rpclib.Server._request, (self, methodname, params))
def __getattr__(self, name):
return rpclib.xmlrpclib._Method(self._request, name)
0707010000002E000081B4000000000000000000000001625E850000004368000000000000000000000000000000000000002600000000mgr-cfg/config_common/transactions.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 shutil
import pwd
import grp
import sys
import errno
import shutil
from config_common import file_utils, utils, cfg_exceptions
from config_common.rhn_log import log_debug
from uyuni.common.usix import raise_with_tb
class TargetNotFile(Exception): pass
class DuplicateDeployment(Exception): pass
class FailedRollback(Exception): pass
try:
from selinux import lsetfilecon
except:
def lsetfilecon(temp_file_path, sectx):
# Do nothing RHEL 4
return 0
BACKUP_PREFIX = '/var/lib/rhncfg/backups'
BACKUP_EXTENSION = '.rhn-cfg-backup'
class DeployTransaction:
def __init__(self, transaction_root=None, auto_rollback=0):
# rollback transaction immediately upon failure?
self.auto_rollback = auto_rollback
# prepend all given paths
self.transaction_root = transaction_root
self.files = []
self.dirs = []
self.symlinks = []
self.new_dirs = []
self.backup_by_path = {}
self.newtemp_by_path = {}
self.changed_dir_info = {}
self.deployment_cb = None
def _generate_backup_path(self, path):
return "%s%s%s" % (BACKUP_PREFIX, path, BACKUP_EXTENSION)
def _rename_to_backup(self, path):
"""renames a file to it's new backup name"""
# ensure we haven't attempted to back this file up before
# (protect against odd logic coming from the server)
if path in self.backup_by_path:
raise DuplicateDeployment("Error: attempted to backup %s twice" % path)
new_path = None
if os.path.exists(path):
# race
if os.path.isfile(path) or os.path.islink(path):
new_path = self._generate_backup_path(path)
log_debug(6, "renaming %s to backup %s ..." % (path, new_path))
# os.renames will fail if the path and the new_path are on different partitions
# need to make sure to handle it if we catch a 'OSError: [Errno 18] Invalid cross-device link'
try:
log_debug(9, "trying to use os.renames")
oumask = os.umask(int('022', 8))
os.renames(path, new_path)
os.umask(oumask)
except OSError:
e = sys.exc_info()[1]
if e.errno == 18:
log_debug(9, "os.renames failed, using shutil functions")
path_dir, path_file = os.path.split(path)
new_path_dir, new_path_file = os.path.split(new_path)
if os.path.isdir(new_path_dir):
if os.path.islink(path):
log_debug(9, "copying symlink %s to %s"% (path,new_path_dir))
linkto = os.readlink(path)
if os.path.lexists(new_path):
log_debug(9, "backup %s exists, removing it"% (new_path))
os.unlink(new_path)
os.symlink(linkto,new_path)
else:
log_debug(9, "backup directory %s exists, copying %s to it" % (new_path_dir, new_path_file))
if os.path.lexists(new_path):
log_debug(9, "backup %s exists, removing it"% (new_path))
os.unlink(new_path)
shutil.copy(path, new_path)
else:
log_debug(9, "backup directory does not exist, creating the tree now")
shutil.copytree(path_dir, new_path_dir, symlinks=0)
shutil.copy(path, new_path)
else:
raise
self.backup_by_path[path] = new_path
log_debug(9, "backed up to %s" % new_path)
else:
raise TargetNotFile("Error: %s is not a valid file, cannot create backup copy" % path)
return new_path
def deploy_callback(self, cb):
self.deployment_cb = cb
def _chown_chmod_chcon(self, temp_file_path, dest_path, file_info, strict_ownership=1):
if file_info['filetype'] != 'symlink':
uid = file_info.get('uid')
if uid is None:
if 'username' in file_info:
# determine uid
try:
user_record = pwd.getpwnam(file_info['username'])
uid = user_record[2]
except Exception:
e = sys.exc_info()[1]
#Check if username is an int
try:
uid = int(file_info['username'])
except ValueError:
raise_with_tb(cfg_exceptions.UserNotFound(file_info['username']), sys.exc_info()[2])
else:
#default to root (3.2 sats)
uid = 0
gid = file_info.get('gid')
if gid is None:
if 'groupname' in file_info:
# determine gid
try:
group_record = grp.getgrnam(file_info['groupname'])
gid = group_record[2]
except Exception:
e = sys.exc_info()[1]
try:
gid = int(file_info['groupname'])
except ValueError:
raise_with_tb(cfg_exceptions.GroupNotFound(file_info['groupname']), sys.exc_info()[2])
else:
#default to root (3.2 sats)
gid = 0
try:
if file_info['filetype'] != 'symlink':
os.chown(temp_file_path, uid, gid)
mode = '600'
if 'filemode' in file_info:
if file_info['filemode'] is "":
mode='000'
else:
mode = file_info['filemode']
mode = int(str(mode), 8)
os.chmod(temp_file_path, mode)
if 'selinux_ctx' in file_info:
sectx = file_info.get('selinux_ctx')
if sectx is not None and sectx is not "":
log_debug(1, "selinux context: " + sectx);
try:
if lsetfilecon(temp_file_path, sectx) < 0:
raise Exception("failed to set selinux context on %s" % dest_path)
except OSError:
e = sys.exc_info()[1]
raise_with_tb(Exception("failed to set selinux context on %s" % dest_path, e), sys.exc_info()[2])
except OSError:
e = sys.exc_info()[1]
if e.errno == errno.EPERM and not strict_ownership:
sys.stderr.write("cannonical file ownership and permissions lost on %s\n" % dest_path)
else:
raise
def _normalize_path_to_root(self, path):
if self.transaction_root:
path = utils.normalize_path(self.transaction_root + os.sep + path)
return path
def add_preprocessed(self, dest_path, processed_file_path, file_info, dirs_created, strict_ownership=1):
"""preprocess the file if needed, and add the entry to the correct list"""
dest_path = self._normalize_path_to_root(dest_path)
log_debug(3, "preprocessing entry")
# If we get any dirs that were created by mkdir_p, add them here
if dirs_created:
self.new_dirs.extend(dirs_created)
# If the file is a directory, don't do all the file related work
# Older servers will not return directories; if filetype is missing,
# assume file
if file_info.get('filetype') == 'directory':
self.dirs.append(file_info)
else:
if "dest_path" in self.newtemp_by_path:
raise DuplicateDeployment("Error: %s already added to transaction" % dest_path)
self.newtemp_by_path[dest_path] = processed_file_path
self._chown_chmod_chcon(processed_file_path, dest_path, file_info, strict_ownership=strict_ownership)
def add(self, file_info):
"""add a file to the deploy transaction"""
for k in file_utils.FileProcessor.file_struct_fields:
if k not in file_info:
raise Exception("needed key %s mising from file structure" % k)
file_info['path'] = self._normalize_path_to_root(file_info['path'])
# Older servers will not return directories; if filetype is missing,
# assume file
if file_info.get('filetype') == 'directory':
self.dirs.append(file_info)
elif file_info.get('filetype') == 'symlink':
self.files.append(file_info)
else:
self.files.append(file_info)
def rollback(self):
"""revert the transaction"""
log_debug(3, "rolling back")
# restore old file from backup asap
for path in self.backup_by_path.keys():
log_debug(6, "restoring %s from %s ..." % (path, self.backup_by_path[path]))
# os.rename will fail if the backup file and the old file are on different partitions
# need to make sure to handle it if we catch a 'OSError: [Errno 18] Invalid cross-device link'
try:
os.rename(self.backup_by_path[path], path)
except OSError:
e = sys.exc_info()[1]
if e.errno == 18:
log_debug(9, "os.rename failed, using shutil.copy")
shutil.copy(self.backup_by_path[path], path)
else:
raise
log_debug(9, "%s restored" % path)
# remove the temp files that we created
for tmp_file_path in self.newtemp_by_path.values():
log_debug(6, "removing tmp file %s ..." % tmp_file_path)
os.unlink(tmp_file_path)
log_debug(9, "tmp file removed")
#revert the owner/perms of any directories that we changed
for d, val in self.changed_dir_info.items():
log_debug(6, "reverting owner and perms of %s" % d)
self._chown_chmod_chcon(d, d, val)
log_debug(9, "directory reverted")
#remove any directories created by either mkdir_p or in the deploy
self.new_dirs.reverse()
for i in range(len(self.new_dirs)):
remove_dir = self.new_dirs[i]
log_debug(6, "removing directory %s that was created during transaction ..." % remove_dir)
if os.path.islink(remove_dir) == True:
os.remove(remove_dir)
else:
os.rmdir(remove_dir)
log_debug(9, "directory removed")
log_debug(3, "rollback successful")
def deploy(self):
"""attempt deployment; will rollback if auto_rollback is set"""
fp = file_utils.FileProcessor()
log_debug(3, "deploying transaction")
for dep_file in self.files:
if dep_file['filetype'] == 'symlink':
self.symlinks.append(dep_file)
# 0. handle any dirs we need to create first
# a) if the dir exists, then just change the mode and owners,
# else create it and then make sure the mode and owners are correct.
# b) if there are files, then continue
# 1. write new version (tmp)
# a) if anything breaks, remove all tmp versions and error out
# 2. rename old version to backup
# a) if anything breaks, rename all backed up files to original name,
# then do 1-a.
# 3. rename tmp to target name
# a) if anything breaks, remove all deployed files, then do 2-a.
#
# (yes, this leaves the backup version on disk...)
try:
# 0.
if self.dirs:
for directory in self.dirs:
dirname = self._normalize_path_to_root(directory['path'])
dirmode = directory['filemode']
if os.path.isfile(dirname):
raise cfg_exceptions.DirectoryEntryIsFile(dirname)
if os.path.isdir(dirname):
s = os.stat(dirname)
entry = { 'filemode': "%o" % (s[0] & int('07777', 8)),
'uid': s[4],
'gid': s[5],
'filetype': 'directory',
}
self.changed_dir_info[dirname] = entry
log_debug(3, "directory found, chowning and chmoding to %s as needed: %s" % (dirmode, dirname))
self._chown_chmod_chcon(dirname, dirname, directory)
else:
log_debug(3, "directory not found, creating: %s" % dirname)
dirs_created = utils.mkdir_p(dirname, None, self.symlinks, self.files)
self.new_dirs.extend(dirs_created)
self._chown_chmod_chcon(dirname, dirname, directory)
if self.deployment_cb:
self.deployment_cb(dirname)
log_debug(6, "changed_dir_info: %s" % self.changed_dir_info)
log_debug(4, "new_dirs: ", self.new_dirs)
if not self.newtemp_by_path and not self.files:
log_debug(4, "directory creation complete, no files found to create")
return
else:
log_debug(4, "done with directory creation, moving on to files")
# 1.
for dep_file in self.files:
path = dep_file['path']
log_debug(6, "writing new version of %s to tmp file ..." % path)
# make any directories needed...
#
# TODO: it'd be nice if this had a hook for letting me know
# which ones are created... then i could clean created
# dirs on rollback
(directory, filename) = os.path.split(path)
if os.path.isdir(path) and not os.path.islink(path):
raise cfg_exceptions.FileEntryIsDirectory(path)
if not os.path.exists(directory):# and os.path.isdir(directory):
log_debug(7, "creating directories for %s ..." % directory)
dirs_created = utils.mkdir_p(directory, None, self.symlinks, self.files)
self.new_dirs.extend(dirs_created)
log_debug(7, "directories created and added to list for rollback")
# write the new contents to a tmp file, and store the path of the
# new tmp file by it's eventual target path
self.newtemp_by_path[path], temp_new_dirs = fp.process(dep_file, os.path.sep)
self.new_dirs.extend(temp_new_dirs or [])
# properly chown and chmod it
self._chown_chmod_chcon(self.newtemp_by_path[path], path, dep_file)
log_debug(9, "tempfile written: %s" % self.newtemp_by_path[path])
#paths = map(lambda x: x['path'], self.files)
paths = list(self.newtemp_by_path.keys())
# 2.
for path in paths:
if os.path.isdir(path) and not os.path.islink(path):
raise cfg_exceptions.FileEntryIsDirectory(path)
else:
self._rename_to_backup(path)
if path in self.backup_by_path:
log_debug(9, "backup file %s written" % self.backup_by_path[path])
# 3.
paths.sort(key = lambda s: s.count(os.path.sep))
for path in paths:
if self.deployment_cb:
self.deployment_cb(path)
log_debug(6, "deploying %s ..." % path)
os.rename(self.newtemp_by_path[path], path)
# race
del self.newtemp_by_path[path]
log_debug(9, "new version of %s deployed" % path)
log_debug(3, "deploy transaction successful")
except Exception:
#log_debug(1, traceback.format_exception_only(SyntaxError, e))
#traceback.print_exc()
if self.auto_rollback:
self.rollback()
raise
0707010000002F000081B4000000000000000000000001625E850000001702000000000000000000000000000000000000001F00000000mgr-cfg/config_common/utils.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 os
# guaranteed to exist even on RHEL 5 because we now require python-hashlib
import hashlib
import re
import shutil
import pwd
import sys
try: # python2
import urlparse
except ImportError: # python3
import urllib.parse as urlparse
import inspect
from config_common.rhn_log import log_debug
from rhn.stringutils import bstr
hashlib_has_usedforsecurity = False
if not hasattr(inspect, 'getfullargspec'):
inspect.getfullargspec = inspect.getargspec
if 'usedforsecurity' in inspect.getfullargspec(hashlib.new)[0]:
hashlib_has_usedforsecurity = True
_normpath_re = re.compile("^(%s)+" % os.sep)
def normalize_path(path):
"""
os.path.normpath does not remove path separator duplicates at the
beginning of the path
"""
return _normpath_re.sub(os.sep, os.path.normpath(path))
def join_path(*args):
return normalize_path(os.sep.join(args))
def path_full_split(path):
"""
Given a path, it fully splits it into constituent path
components (as opposed to os.path.split which splits it into
trailing component and preceeding path
"""
path = normalize_path(path)
splitpath = []
while 1:
path, current = os.path.split(path)
if current == '':
if path:
# Absolute path
splitpath.append(os.sep)
break
splitpath.append(current)
splitpath.reverse()
return splitpath
def copyfile_p(src, dst):
"""
Simple util function, copies src path to dst path, making
directories as necessary. File permissions are not preserved.
"""
directory = os.path.split(dst)[0]
try:
os.makedirs(directory)
except OSError:
e = sys.exc_info()[1]
if e.errno != 17:
# not File exists
raise
if os.path.isdir(src):
if not os.path.exists(dst):
os.mkdir(dst)
elif os.path.islink(src):
exists = hasattr(os.path, "lexists") and os.path.lexists or os.path.exists
if exists(dst):
os.remove(dst)
os.symlink(os.readlink(src), dst)
else:
shutil.copyfile(src, dst)
def mkdir_p(path, mode=None, symlinks=None, allfiles=None):
"""
Similar to 'mkdir -p' -- makes all directories necessary to ensure
the 'path' is a directory, and return the list of directories that were
made as a result
"""
if mode is None:
mode = int('0700', 8)
dirs_created = []
components = path_full_split(path)
for i in range(1,len(components)):
d = os.path.join(*components[:i+1])
if symlinks:
for symlink in symlinks:
if symlink['path'] == d:
# create symlink and remove it from symlink list
os.symlink(symlink['symlink'], symlink['path'])
symlinks.remove(symlink)
allfiles.remove(symlink)
dirs_created.append(symlink)
continue
log_debug(8, "testing", d)
try:
os.mkdir(d, mode)
dirs_created.append(d)
log_debug(8, "created", d)
except OSError:
e = sys.exc_info()[1]
if e.errno != 17:
raise
else:
log_debug(8, "already exists", d)
log_debug(6, "dirs_created:", dirs_created)
return dirs_created
def rmdir_p(path, stoppath):
"""
if rmdir had a -p option, this would be it. remove dir and up
until empty dir is hit, or stoppath is reached
path and stoppath have to be absolute paths
"""
# First normalize both paths
stoppath = normalize_path(os.sep + stoppath)
path = normalize_path(os.sep + path)
# stoppath has to be a prefix of path
if path[:len(stoppath)] != stoppath:
raise OSError("Could not remove %s: %s is not a prefix" % (
path, stoppath))
while 1:
if stoppath == path:
# We're done
break
# Try to remove the directory
try:
os.rmdir(path)
except OSError:
# Either the directory is full, or we don't have permissions; stop
break
path, current = os.path.split(path)
if current == '':
# We're done - reached the root
break
#returns slashstring with any trailing slash removed
def rm_trailing_slash(slashstring):
if slashstring[-1] == "/":
slashstring = slashstring[0:-1]
return slashstring
def getContentChecksum(checksum_type, contents):
if hashlib_has_usedforsecurity:
engine = hashlib.new(checksum_type, usedforsecurity=False)
else:
engine = hashlib.new(checksum_type)
engine.update(bstr(contents))
return engine.hexdigest()
def sha256_file(filename):
engine = hashlib.new('sha256')
fh = open(filename, "rb")
while 1:
buf = fh.read(4096)
if not buf:
break
engine.update(bstr(buf))
return engine.hexdigest()
def parse_url(server_url, scheme="https"):
return urlparse.urlparse(server_url, scheme=scheme)
def unparse_url(url_tuple):
return urlparse.urlunparse(url_tuple)
def get_home_dir():
uid = os.getuid()
ent = pwd.getpwuid(uid)
return ent[5]
07070100000030000041FD000000000000000000000001625E850000000000000000000000000000000000000000000000001A00000000mgr-cfg/config_management07070100000031000081B4000000000000000000000001625E8500000004AE000000000000000000000000000000000000002300000000mgr-cfg/config_management/Makefile# Makefile for spacewalk backend
#
# what is the backend top dir
TOP = ..
# Specific stuff
SUBDIR = config_management
EXEC_FILES = rhncfg-manager
FILES = $(EXEC_FILES) __init__ \
rhncfg_add \
rhncfg_create_channel \
rhncfg_diff \
rhncfg_diff_revisions \
rhncfg_download_channel \
rhncfg_get \
rhncfg_list \
rhncfg_list_channels \
rhncfg_remove \
rhncfg_remove_channel \
rhncfg_revisions \
rhncfg_update \
rhncfg_upload_channel \
rpc_repository
CONFIG_FILES = rhncfg-manager.conf
# man pages
DOCBOOK = $(wildcard /usr/bin/docbook2man)
SGMLS = $(wildcard *.sgml)
MANS = $(patsubst %.sgml,%.8,$(SGMLS))
MANDIR ?= /usr/man
EXTRA_DIRS = $(BINDIR) $(CONFDIR) $(MANDIR)/man8
include $(TOP)/Makefile.defs
ifneq ($(DOCBOOK),)
# install man pages
all :: $(MANS)
install :: $(MANS) $(PREFIX)/$(MANDIR)
$(INSTALL_DATA) $(MANS) $(PREFIX)/$(MANDIR)/man8
clean ::
@rm -fv $(MANS) manpage.*
endif
install :: install-bin install-conf
07070100000032000081B4000000000000000000000001625E850000000266000000000000000000000000000000000000002600000000mgr-cfg/config_management/__init__.py#
# Copyright (c) 2008--2013 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.
#
07070100000033000081B4000000000000000000000001625E850000000524000000000000000000000000000000000000002E00000000mgr-cfg/config_management/rhncfg-manager.conf[rhncfg-manager]
# increase for debugging output
debug_level = 0
# don't change this... used in substitutions below.
# if you get rid of the '%(server_handler)s' bits below,
# the *MUST* be replaced with this value...
server_handler = /CONFIG-MANAGEMENT-TOOL
# don't change this, period.
repository_type = rpc_repository
# to use a server other than what up2date is configured to use,
# do something along the lines of:
# server_url = https://some.example.com%(server_handler)s
# server_url = http://another.example.net:8080%(server_handler)s
# server_url = https://yet.another.example.org:8081/CONFIG-MANAGEMENT-TOOL
#
# You may also edit this value in a ~/.rhncfgrc file override file,
# which will allow you to run the rhncfg-manager program as non-root
server_url = https://%(server_name)s%(server_handler)s
# for your convenience, you may put your RHN username in here
# to avoid being asked for it every time you run rhncfg-manager
# username = your_rhn_username_here
# the following fields are inherited from up2date's configuration,
# but are overridable in this file, or in a ~/.rhncfgrc file
# enableProxy = 1
# enableProxyAuth = 1
# httpProxy = some.proxy.example.com:3030
# proxyUser = proxy_user_name
# proxyPassword = proxy_password
# sslCACert = /usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT
07070100000034000081B4000000000000000000000001625E850000000502000000000000000000000000000000000000002C00000000mgr-cfg/config_management/rhncfg-manager.py#!/usr/bin/python
#
# Copyright (c) 2008--2013 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
from config_common.rhn_main import BaseMain
class Main(BaseMain):
modes = [
'add',
'create-channel',
'diff',
'diff-revisions',
'download-channel',
'get',
'list',
'list-channels',
'remove',
'remove-channel',
'revisions',
'update',
'upload-channel',
]
plugins_dir = 'config_management'
config_section = 'rhncfg-manager'
mode_prefix = 'rhncfg'
if __name__ == '__main__':
try:
sys.exit(Main().main() or 0)
except KeyboardInterrupt:
sys.stderr.write("user interrupted\n")
sys.exit(0)
07070100000035000081B4000000000000000000000001625E8500000033D1000000000000000000000000000000000000002E00000000mgr-cfg/config_management/rhncfg-manager.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!ENTITY RHN "Spacewalk" >
<!ENTITY HOSTED " or Hosted" >
<!ENTITY RHNCFGM "rhncfg-manager" >
]>
<refentry>
<RefMeta>
<RefEntryTitle>&RHNCFGM;</RefEntryTitle><manvolnum>8</manvolnum>
<RefMiscInfo>Version 4.0</RefMiscInfo>
</RefMeta>
<RefNameDiv>
<RefName><command>&RHNCFGM;</command></RefName>
<RefPurpose>
a tool for managing &RHN; central repository of configuration files
and channels.
</RefPurpose>
</RefNameDiv>
<RefSynopsisDiv>
<Synopsis>
<cmdsynopsis>
<command>&RHNCFGM;</command>
<arg>MODE</arg>
<arg>--config <replaceable>config_file</replaceable></arg>
<arg>--server-name <replaceable>name</replaceable></arg>
<arg>--username <replaceable>user</replaceable></arg>
<arg>--password <replaceable>pass</replaceable></arg>
<arg>params</arg>
</cmdsynopsis>
</Synopsis>
</RefSynopsisDiv>
<RefSect1><Title>Description</Title>
<para>
The &RHN; Configuration Manager (<emphasis>&RHNCFGM;</emphasis>) is used to
manage &RHN; configuration files and channels.
</para>
<para>
This tool offers a command line alternative to the configuration management
features found within the &RHN; web user interface&HOSTED;, as well
as the ability to script some or all of the related maintenance.
</para>
<para>
This tool is intended for use by the Config Administrator and therefore
requires an &RHN; username and password that has the appropriate permission
set. (The username and password may be specified in
<filename>/etc/sysconfig/rhn/rhncfg-manager.conf</filename> or in
the [rhncfg-manager] section of <filename>~/.rhncfgrc</filename>
or the config file specified
by <term>--config=<replaceable>config_file</replaceable></term>).
</para>
<para>
Note that when run as root, &RHNCFGM; attempts to pull in needed
configuration values from the Red Hat Update Agent (up2date). When run as
something other than root, configuration changes may be needed within the
<filename>~/.rhncfgrc</filename> file, or the config file
specified
by <term>--config=<replaceable>config_file</replaceable></term>. The
session file is cached in
<filename>~/.rhncfg-manager-session</filename> to prevent logging in for
every command.
</para>
<para>
The <filename>~/.rhncfgrc</filename> file overrides the values set in
<filename>/etc/sysconfig/rhn/rhncfg-manager.conf</filename> which, by
default, uses the values set in
<filename>/etc/sysconfig/rhn/up2date</filename>. The <filename>~/.rhncfgrc</filename>
file may itself be overridden by the command line
option <term>--config=<replaceable>config_file</replaceable></term>
Please review
<filename>rhncfg-manager.conf</filename> for further details.
</para>
<para>
The &RHN; Configuration Manager offers these primary modes:
<emphasis>add</emphasis>, <emphasis>create-channel</emphasis>,
<emphasis>diff</emphasis>, <emphasis>diff-revisions</emphasis>,
<emphasis>download-channel</emphasis>, <emphasis>get</emphasis>,
<emphasis>list</emphasis>, <emphasis>list-channels</emphasis>,
<emphasis>remove</emphasis>, <emphasis>remove-channel</emphasis>,
<emphasis>revisions</emphasis>, <emphasis>update</emphasis>,
<emphasis>upload-channel</emphasis>.
</para>
<para>
Context sensitive help is available for each "mode", e.g.,
&RHNCFGM; diff-revisions --help
</para>
<para>
<simplelist>
<member>This tool utilizes a "cvs style" command syntax: command mode options</member>
<member>For example: &RHNCFGM; diff-revisions --help</member>
</simplelist>
</para>
</RefSect1>
<RefSect1><Title>modes</Title>
<variablelist>
<varlistentry>
<term>add [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
<listitem>
<para>
Add a file to a config channel.
</para>
<para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- upload file(s) in this config channel</para>
<para>-d<replaceable>DEST_FILE</replaceable>, --dest-file=<replaceable>DEST_FILE</replaceable> -- upload the file as this path</para>
<para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
<para>--delim-start=<replaceable>DELIM_START</replaceable> -- start delimiter for variable interpolation</para>
<para>--delim-end=<replaceable>DELIM_END</replaceable> -- end delimiter for variable interpolation</para>
<para>--selinux-context=<replaceable>SELINUX_CONTEXT</replaceable> -- overwrite the SELinux context label to this string</para>
</listitem>
</varlistentry>
<varlistentry>
<term>create-channel <replaceable>CHANNEL_LABEL</replaceable> [ <replaceable>CHANNEL_LABEL ...</replaceable> ]</term>
<listitem>
<para>
Create a config channel for your organization.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>diff [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
<listitem>
<para>
Differentiating between latest configuration files in a channel
and whats on disk.
</para>
<para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- get file(s) from this config channel</para>
<para>-r<replaceable>REVISION</replaceable>, --revision=<replaceable>REVISION</replaceable> -- use this revision</para>
<para>-d<replaceable>DEST_FILE</replaceable>, --dest-file=<replaceable>DEST_FILE</replaceable> -- upload the file as this path</para>
<para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
</listitem>
</varlistentry>
<varlistentry>
<term>diff-revisions [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable></term>
<listitem>
<para>
Compare different versions of a file across channels and
revisions. See documentation for further details.
</para>
<para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- use this config channel</para>
<para>-r<replaceable>REVISION</replaceable>, --revision=<replaceable>REVISION</replaceable> -- use this revision</para>
</listitem>
</varlistentry>
<varlistentry>
<term>download-channel (-t|--topdir=)<replaceable>TOP_LEVEL_DIRECTORY</replaceable> [ <replaceable>OPTIONS</replaceable> ] <replaceable>CHANNEL_LABEL</replaceable> [ <replaceable>CHANNEL_LABEL</replaceable> ... ]</term>
<listitem>
<para>
Download all the configuration files in a channel to disk.
</para>
<para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- directory all the file paths are relative to</para>
</listitem>
</varlistentry>
<varlistentry>
<term>get [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
<listitem>
<para>
Get the contents of a particular configuration file. Outputs
to stdout.
</para>
<para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- get file(s) from this config channel</para>
<para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
<para>-r<replaceable>REVISION</replaceable>, --revision=<replaceable>REVISION</replaceable> -- get this file revision</para>
</listitem>
</varlistentry>
<varlistentry>
<term>list <replaceable>CHANNEL_LABEL</replaceable></term>
<listitem>
<para>
List all configuration files in a channel.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>list-channels</term>
<listitem>
<para>
List all configuration channels.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>remove [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
<listitem>
<para>
Remove a configuration file from a channel. May be prompted for
a username and password.
</para>
<para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- remove file(s) from this config channel</para>
<para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
</listitem>
</varlistentry>
<varlistentry>
<term>remove-channel <replaceable>CHANNEL_LABEL</replaceable> [ <replaceable>CHANNEL_LABEL</replaceable> ... ]</term>
<listitem>
<para>
Remove a configuration channel. May be prompted for
a username and password.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>revisions [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
<listitem>
<para>
Determine the number of configuration file revisions.
</para>
<para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- use this config channel</para>
</listitem>
</varlistentry>
<varlistentry>
<term>update [ <replaceable>OPTIONS</replaceable> ] <replaceable>FILE</replaceable> [ <replaceable>FILE</replaceable> ... ]</term>
<listitem>
<para>
Create a new revision of a file in a channel (or add the first
revisions to that channel if none existed before for the given
path).
</para>
<para>-c<replaceable>CHANNEL_LABEL</replaceable>, --channel=<replaceable>CHANNEL_LABEL</replaceable> -- upload file(s) in this config channel</para>
<para>-d<replaceable>DEST_FILE</replaceable>, --dest-file=<replaceable>DEST_FILE</replaceable> -- upload the file as this path</para>
<para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- make all files relative to this string</para>
<para>--delim-start=<replaceable>DELIM_START</replaceable> -- start delimiter for variable interpolation</para>
<para>--delim-end=<replaceable>DELIM_END</replaceable> -- end delimiter for variable interpolation</para>
<para>--selinux-context=<replaceable>SELINUX_CONTEXT</replaceable> -- overwrite the SELinux context label to this string</para>
</listitem>
</varlistentry>
<varlistentry>
<term>upload-channel [ <replaceable>OPTIONS</replaceable> ] <replaceable>CHANNEL_LABEL</replaceable> [ <replaceable>CHANNEL_LABEL</replaceable> ... ]</term>
<listitem>
<para>
Upload multiple configuration files (a tree) to a channel from
disk.
</para>
<para>-t<replaceable>TOPDIR</replaceable>, --topdir=<replaceable>TOPDIR</replaceable> -- directory all file paths are relative to</para>
</listitem>
</varlistentry>
</variablelist>
</RefSect1>
<RefSect1><Title>others</Title>
<variablelist>
<varlistentry>
<term>--server-name</term>
<listitem>
<para>&RHN; server name.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--username</term>
<listitem>
<para>&RHN; user name.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--password</term>
<listitem>
<para>&RHN; password for the specified user.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-h, --help</term>
<listitem>
<para>Display the help screen with a list of options.</para>
<para>Use context sensitive help as well with the diffent "modes".
E.g., &RHNCFGM; upload-channel --help</para>
</listitem>
</varlistentry>
</variablelist>
</RefSect1>
<RefSect1><Title>See Also</Title>
<simplelist>
<member>rhncfg-client (8)</member>
</simplelist>
</RefSect1>
<RefSect1><Title>Authors</Title>
<simplelist>
<member>Mihai Ibanescu <email>misa@redhat.com</email></member>
<member>Bret McMillan <email>bretm@redhat.com</email></member>
<member>Todd Warner <email>taw@redhat.com</email> (man page only)</member>
<member>Laurence Rochfort <email>laurence.rochfort@oracle.com</email></member>
</simplelist>
</RefSect1>
</RefEntry>
07070100000036000081B4000000000000000000000001625E85000000051B000000000000000000000000000000000000002600000000mgr-cfg/config_management/rhncfg.conf[rhncfg]
# increase for debugging output
debug_level = 0
# don't change this... used in substitutions below.
# if you get rid of the '%(server_handler)s' bits below,
# the *MUST* be replaced with this value...
server_handler = /CONFIG-MANAGEMENT-TOOL
# don't change this, period.
repository_type = rpc_repository
# to use a server other than what up2date is configured to use,
# do something along the lines of:
# server_url = https://some.example.com%(server_handler)s
# server_url = http://another.example.net:8080%(server_handler)s
# server_url = https://yet.another.example.org:8081/CONFIG-MANAGEMENT-TOOL
#
# You may also edit this value in a ~/.rhncfgrc file override file,
# which will allow you to run the rhncfg-manager program as non-root
server_url = https://%(server_name)s%(server_handler)s
# for your convenience, you may put your RHN username in here
# to avoid being asked for it every time you run rhncfg-manager
# username = your_rhn_username_here
# the following fields are inherited from up2date's configuration,
# but are overridable in this file, or in a ~/.rhncfgrc file
# enableProxy = 1
# enableProxyAuth = 1
# httpProxy = some.proxy.example.com:3030
# proxyUser = proxy_user_name
# proxyPassword = proxy_password
# sslCACert = /usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT
07070100000037000081B4000000000000000000000001625E8500000015D8000000000000000000000000000000000000002800000000mgr-cfg/config_management/rhncfg_add.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 os
import sys
from config_common import handler_base, utils, cfg_exceptions
from config_common.rhn_log import log_debug, die, log_error
class Handler(handler_base.HandlerBase):
_usage_options = "[options] file [ file ... ]"
is_first_revision=1
_options_table = handler_base.HandlerBase._options_table + [
handler_base.HandlerBase._option_class(
'-c', '--channel', action="store",
help="Upload files in this config channel",
),
handler_base.HandlerBase._option_class(
'-d', '--dest-file', action="store",
help="Upload the file as this path",
),
handler_base.HandlerBase._option_class(
'-t', '--topdir', action="store",
help="Make all files relative to this string",
),
handler_base.HandlerBase._option_class(
'--delim-start', action="store",
help="Start delimiter for variable interpolation",
),
handler_base.HandlerBase._option_class(
'--delim-end', action="store",
help="End delimiter for variable interpolation",
),
handler_base.HandlerBase._option_class(
'-i', '--ignore-missing', action="store_true",
help="Ignore missing local files",
),
handler_base.HandlerBase._option_class(
'--selinux-context', action="store",
help="Overwrite the SELinux context",
),
]
def run(self):
log_debug(2)
if self.options.dest_file and self.options.topdir:
die(6, "Error: conflicting options --dest-file and --topdir")
if len(self.args) == 0:
die(0, "No files supplied (use --help for help)")
channel = self.options.channel
if not channel:
die(6, "Config channel not specified")
r = self.repository
if not r.config_channel_exists(channel):
die(6, "Error: config channel %s does not exist" % channel)
files = [utils.normalize_path(x) for x in self.args]
files_to_push = []
if self.options.dest_file:
if len(files) != 1:
die(7, "--dest-file accepts a single file")
if not (self.options.dest_file[0] == os.sep):
die(7, "--dest-file argument must begin with " + os.sep)
files_to_push.append((files[0], self.options.dest_file))
elif self.options.topdir:
if not os.path.isdir(self.options.topdir):
die(8, "--topdir specified, but `%s' not a directory" %
self.options.topdir)
#5/11/05 wregglej - 141790 remove the trailing slash from topdir
self.options.topdir = utils.rm_trailing_slash(self.options.topdir)
for f in files:
if not f.startswith(self.options.topdir):
die(8, "--topdir %s specified, but file `%s' doesn't comply"
% (self.options.topdir, f))
files_to_push.append((f, f[len(self.options.topdir):]))
else:
for f in files:
#if a file is given w/o a full path, then use the abspath of the
#file as name of the file to be uploaded into the channel
if not (f[0] == os.sep):
files_to_push.append((f, os.path.abspath(f)))
else:
files_to_push.append((f, f))
for (local_file, remote_file) in files_to_push:
if not os.path.exists(local_file):
if self.options.ignore_missing:
files_to_push.remove((local_file,remote_file))
print("Local file %s does not exist. Ignoring file..." %(local_file))
else:
die(9, "No such file `%s'" % local_file)
print("Pushing to channel %s:" % (channel, ))
delim_start = self.options.delim_start
delim_end = self.options.delim_end
selinux_ctx = None
if type(self.options.selinux_context) != None:
selinux_ctx = self.options.selinux_context
for (local_file, remote_file) in files_to_push:
try:
r.put_file(channel, remote_file, local_file,
is_first_revision=self.is_first_revision,
delim_start=delim_start,
delim_end=delim_end,
selinux_ctx=selinux_ctx)
except cfg_exceptions.RepositoryFileExistsError:
e = sys.exc_info()[1]
log_error("Error: %s is already in channel %s" %
(remote_file, channel))
except cfg_exceptions.RepositoryFilePushError:
e = sys.exc_info()[1]
log_error("Error pushing file: %s" % e)
else:
print("Local file %s -> remote file %s" % (local_file, remote_file))
07070100000038000081B4000000000000000000000001625E85000000052A000000000000000000000000000000000000003300000000mgr-cfg/config_management/rhncfg_create_channel.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.
#
from config_common import handler_base, cfg_exceptions
from config_common.rhn_log import log_debug, die
class Handler(handler_base.HandlerBase):
_usage_options = "config_channel [ config_channel ... ]"
def run(self):
log_debug(2)
r = self.repository
if len(self.args) == 0:
die(5, "No config channels specified")
for channel in self.args:
print("Creating config channel %s" % channel)
try:
r.create_config_channel(channel)
except cfg_exceptions.ConfigChannelAlreadyExistsError:
die("Config channel %s already exists" % channel)
print("Config channel %s created" % channel)
07070100000039000081B4000000000000000000000001625E85000000204B000000000000000000000000000000000000002900000000mgr-cfg/config_management/rhncfg_diff.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 base64
from datetime import datetime
import difflib
import os
import sys
from config_common import handler_base, utils, cfg_exceptions
from config_common.rhn_log import log_debug, die
from config_common.file_utils import f_date, ostr_to_sym
from uyuni.common.usix import next
from rhn.stringutils import bstr, sstr
class Handler(handler_base.HandlerBase):
_usage_options = "[options] file [ file ... ]"
_options_table = [
handler_base.HandlerBase._option_class(
'-c', '--channel', action="store",
help="Get file(s) from this config channel",
),
handler_base.HandlerBase._option_class(
'-r', '--revision', action="store",
help="Use this revision",
),
handler_base.HandlerBase._option_class(
'-d', '--dest-file', action="store",
help="Remote file to compare to",
),
handler_base.HandlerBase._option_class(
'-t', '--topdir', action="store",
help="Directory to which all file paths are relative",
),
]
def run(self):
log_debug(2)
if self.options.dest_file and self.options.topdir:
die(6, "Error: conflicting options --dest-file and --topdir")
if len(self.args) == 0:
die(0, "No files supplied (use --help for help)")
channel = self.options.channel
if not channel:
die(6, "Config channel not specified")
r = self.repository
if not r.config_channel_exists(channel):
die(6, "Error: config channel %s does not exist" % channel)
topdir = self.options.topdir
revision = self.options.revision
files_to_diff = []
files = [utils.normalize_path(x) for x in self.args]
files_count = len(files)
if files_count != 1 and revision is not None:
die(8, "--revision can only be used with a single file")
if self.options.dest_file:
if files_count != 1:
die(7, "--dest-file accepts a single file")
files_to_diff.append((files[0], self.options.dest_file))
elif topdir:
if not os.path.isdir(topdir):
die(8, "--topdir specified, but `%s' not a directory" %
topdir)
#5/11/04 wregglej - 141790 remove trailing slash in topdir, if present.
topdir = utils.rm_trailing_slash(topdir)
for f in files:
if not f.startswith(topdir):
die(8, "--topdir %s specified, but file `%s' doesn't comply"
% (topdir, f))
if os.path.isdir(f) and not os.path.islink(f):
die(8, "Cannot diff %s; it is a directory" % f)
files_to_diff.append((f, f[len(topdir):]))
else:
for f in files:
if os.path.isdir(f) and not os.path.islink(f):
die(8, "Cannot diff %s; it is a directory" % f)
files_to_diff.append((f, f))
for (local_file, remote_file) in files_to_diff:
sys.stdout.write(
self.diff_file(channel, remote_file, local_file, revision))
def __attributes_differ(self, fsrc, fdst):
""" Returns true if acl, ownership, type or selinux context differ.
fsrc is config file retrieved from xmlrpc, fdst is output of make_stat_info()
"""
return (fsrc['filemode'] != fdst['mode']) or \
(fsrc['username'] != fdst['user']) or (fsrc['groupname'] != fdst['group']) or \
(fsrc['selinux_ctx'] != fdst['selinux_ctx'])
def diff_file(self, channel, path, local_file, revision):
r = self.repository
try:
info = r.get_raw_file_info(channel, path, revision)
if 'encoding' in info and info['file_contents']:
if info['encoding'] == 'base64':
info['file_contents'] = base64.decodestring(bstr(info['file_contents']))
else:
die(9, 'Error: unknown encoding %s' % info['encoding'])
except cfg_exceptions.RepositoryFileMissingError:
die(2, "Error: no such file %s (revision %s) in config channel %s"
% (path, revision, channel))
if os.path.islink(local_file) and info['filetype'] != 'symlink' :
die(8, "Cannot diff %s; the file on the system is a symbolic link while the file in the channel is not. " % local_file)
if info['filetype'] == 'symlink' and not os.path.islink(local_file) :
die(8, "Cannot diff %s; the file on the system is not a symbolic link while the file in the channel is. " % local_file)
if info['filetype'] == 'symlink':
src_link = info['symlink']
dest_link = os.readlink(local_file)
if src_link != os.readlink(local_file):
return "Symbolic links differ. Channel: '%s' -> '%s' System: '%s' -> '%s' \n " % (path,src_link, path, dest_link)
return ""
response_output = ""
content_differs = False
if 'is_binary' in info and info['is_binary'] == 'Y':
from_content = info['file_contents']
to_file = open(local_file, 'rb')
to_content = to_file.read()
to_file.close()
if len(from_content) != len(to_content):
content_differs = True
else:
for i in range(len(from_content)):
if from_content[i] != to_content[i]:
content_differs = True
break
if content_differs:
response_output = "Binary file content differs\n"
else:
fromlines = sstr(info['file_contents']).splitlines(1)
tofile = open(local_file, 'r')
tolines = tofile.readlines()
tofile.close()
diff_output = difflib.unified_diff(fromlines, tolines, info['path'], local_file)
first_row = second_row = ''
try:
first_row = next(diff_output)
# if content was same, exception thrown so following
# lines don't execute
content_differs = True
second_row = next(diff_output)
response_output = ''.join(list(diff_output))
except StopIteration:
pass
file_stat = os.lstat(local_file)
local_info = r.make_stat_info(local_file, file_stat)
# rhel4 do not support selinux
if not 'selinux_ctx' in local_info:
local_info['selinux_ctx'] = ''
if 'selinux_ctx' not in info:
info['selinux_ctx'] = ''
if not content_differs and not self.__attributes_differ(info, local_info):
return ""
else:
template = "%s %s\t%s\tattributes: %s %s %s %s\tconfig channel: %s\trevision: %s"
if 'modified' not in info:
info['modified'] = ''
first_row = template % ('---', path, str(info['modified']), ostr_to_sym(info['filemode'], info['filetype']),
info['username'], info['groupname'], info['selinux_ctx'], channel,
info['revision'],
)
second_row = template % ('+++', local_file, f_date(datetime.fromtimestamp(local_info['mtime'])), ostr_to_sym(local_info['mode'], 'file'),
local_info['user'], local_info['group'], local_info['selinux_ctx'], 'local file', None
)
return first_row + '\n' + second_row + '\n' + response_output
0707010000003A000081B4000000000000000000000001625E850000000ACD000000000000000000000000000000000000003300000000mgr-cfg/config_management/rhncfg_diff_revisions.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
from config_common import handler_base, cfg_exceptions
from config_common.rhn_log import log_debug, die
class Handler(handler_base.HandlerBase):
_usage_options = "[options] file"
_options_table = [
handler_base.HandlerBase._option_class(
'-c', '--channel', action="append",
help="Use this config channel",
),
handler_base.HandlerBase._option_class(
'-r', '--revision', action="append",
help="Use this revision",
),
]
def run(self):
log_debug(2)
r = self.repository
if len(self.args) != 1:
die(3, "One file needs to be specified")
path = self.args[0]
channel_dst = None
ns_count = len(self.options.channel or [])
if ns_count == 0:
die(3, "At least one config channel has to be specified")
channel_src = self.options.channel[0]
if ns_count > 2:
die(3, "At most two config channels can be specified")
if not r.config_channel_exists(channel_src):
die(4, "Source config channel %s does not exist" % channel_src)
if ns_count == 2:
channel_dst = self.options.channel[1]
if not r.config_channel_exists(channel_dst):
die(4, "Config channel %s does not exist" % channel_dst)
revision_dst = None
rev_count = len(self.options.revision or [])
if rev_count == 0:
die(3, "At least one revision has to be specified")
revision_src = self.options.revision[0]
if rev_count > 2:
die(3, "At most two revisions can be specified")
if rev_count == 2:
revision_dst = self.options.revision[1]
try:
result = r.diff_file_revisions(path, channel_src,
revision_src, channel_dst, revision_dst)
except cfg_exceptions.RepositoryFileMissingError:
e = sys.exc_info()[1]
die(2, e[0])
except cfg_exceptions.BinaryFileDiffError:
e = sys.exc_info()[1]
die(3, e[0])
sys.stdout.write(result)
0707010000003B000081B4000000000000000000000001625E850000000734000000000000000000000000000000000000003500000000mgr-cfg/config_management/rhncfg_download_channel.py#
# Copyright (c) 2008--2013 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
from config_common import handler_base, utils
from config_common.deploy import deploy_files
from config_common.rhn_log import log_debug, die
class Handler(handler_base.HandlerBase):
_usage_options = "(-t|--topdir=)<top-level-directory> [options] config_channel [ config_channel ... ]"
_options_table = handler_base.HandlerBase._options_table + [
handler_base.HandlerBase._option_class(
'-t', '--topdir', action="store",
help="Directory all the file paths are relative to. This option must be set.",
),
]
def run(self):
log_debug(2)
r = self.repository
if not self.args:
die(6, "No config channels specified")
topdir = self.options.topdir
if not topdir:
die(7, "--topdir not specified")
if not os.path.isdir(topdir):
die(8, "--topdir specified, but `%s' not a directory" %
topdir)
for ns in self.args:
if not r.config_channel_exists(ns):
die(6, "Error: config channel %s does not exist" % ns)
deploy_files(utils.join_path(topdir, ns), r, r.list_files(ns),
config_channel=ns)
0707010000003C000081B4000000000000000000000001625E8500000011EA000000000000000000000000000000000000002800000000mgr-cfg/config_management/rhncfg_get.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 os
import sys
import tempfile
from config_common import handler_base, utils, cfg_exceptions
from config_common.rhn_log import log_debug, die
from config_common.transactions import DeployTransaction, FailedRollback
from uyuni.common.usix import raise_with_tb
def deploying_mesg_callback(path):
print("Deploying %s" % path)
class Handler(handler_base.HandlerBase):
_usage_options = "[options] file [ file ... ]"
_options_table = handler_base.HandlerBase._options_table + [
handler_base.HandlerBase._option_class(
'-c', '--channel', action="store",
help="Get file(s) from this config channel",
),
handler_base.HandlerBase._option_class(
'-t', '--topdir', action="store",
help="Make all files relative to this string",
),
handler_base.HandlerBase._option_class(
'-r', '--revision', action="store",
help="Get this file revision",
),
]
def run(self):
log_debug(2)
r = self.repository
channel = self.options.channel
if not channel:
die(6, "Config channel not specified")
topdir = self.options.topdir
if topdir:
if not os.path.isdir(self.options.topdir):
die(8, "--topdir specified, but `%s' not a directory" %
self.options.topdir)
if not self.args:
die(7, "No files specified")
revision = self.options.revision
if revision:
if len(self.args) > 1:
die(9, "--revision specified with multiple files")
dep_trans = None
if topdir:
dep_trans = DeployTransaction(transaction_root=topdir)
dep_trans.deploy_callback(deploying_mesg_callback)
for f in self.args:
try:
directory = topdir or tempfile.gettempdir()
#5/11/05 wregglej - 157066 dirs_created is returned from get_file_info.
(temp_file, info, dirs_created) = r.get_file_info(channel, f, revision=revision,
auto_delete=0, dest_directory=directory)
except cfg_exceptions.RepositoryFileMissingError:
if revision is not None:
die(2, "Error: file %s (revision %s) not in config "
"channel %s" % (f, revision, channel))
else:
die(2, "Error: file %s not in config channel %s" %
(f, channel))
if topdir:
#5/11/05 wregglej - 157066 dirs_created now gets passed into add_preprocessed.
dep_trans.add_preprocessed(f, temp_file, info, dirs_created, strict_ownership=0)
continue
elif info.get('filetype') == 'symlink':
print("%s -> %s" % (info['path'], info['symlink']))
continue
elif info.get('filetype') == 'directory':
print("%s is a directory entry, nothing to get" % info['path'])
continue
else:
print(open(temp_file).read())
os.unlink(temp_file)
if topdir:
try:
dep_trans.deploy()
except Exception:
try:
dep_trans.rollback()
except FailedRollback:
raise_with_tb(FailedRollback("FAILED ROLLBACK: "), sys.exc_info()[2])
#5/3/05 wregglej - 136415 Added exception stuff for missing user info.
except cfg_exceptions.UserNotFound:
raise
#5/5/05 wregglej - 136415 Added exception handling for unknown group.
except cfg_exceptions.GroupNotFound:
raise
else:
raise_with_tb(Exception("Deploy failed, rollback successful: "), sys.exc_info()[2])
0707010000003D000081B4000000000000000000000001625E8500000004C2000000000000000000000000000000000000002900000000mgr-cfg/config_management/rhncfg_list.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.
#
from config_common import handler_base
from config_common.rhn_log import log_debug, die
class Handler(handler_base.HandlerBase):
_usage_options = "config_channel"
def run(self):
log_debug(2)
r = self.repository
if len(self.args) != 1:
die(5, "Exactly one argument required")
channel = self.args[0]
print("Files in config channel '%s':" % channel)
files = r.list_files(channel)
if not files:
print("(no files in config channel)")
else:
for file in files:
print(" %s" % file)
0707010000003E000081B4000000000000000000000001625E850000000404000000000000000000000000000000000000003200000000mgr-cfg/config_management/rhncfg_list_channels.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.
#
from config_common import handler_base
from config_common.rhn_log import log_debug, die
class Handler(handler_base.HandlerBase):
_usage_options = ""
def run(self):
log_debug(2)
r = self.repository
if len(self.args):
die(5, "No arguments required")
print("Available config channels:")
for ns in r.list_config_channels():
print(" %s" % ns)
0707010000003F000081B4000000000000000000000001625E850000000B71000000000000000000000000000000000000002B00000000mgr-cfg/config_management/rhncfg_remove.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 os
import sys
from config_common import handler_base, utils
from config_common.rhn_log import log_debug, die
try: # python2
import xmlrpclib
except ImportError: # python3
import xmlrpc.client as xmlrpclib
class Handler(handler_base.HandlerBase):
_usage_options = "[options] file [ file ... ]"
_options_table = handler_base.HandlerBase._options_table + [
handler_base.HandlerBase._option_class(
'-c', '--channel', action="store",
help="Remove files from this config channel",
),
handler_base.HandlerBase._option_class(
'-t', '--topdir', action="store",
help="Make all files relative to this string",
),
]
def run(self):
log_debug(2)
r = self.repository
if len(self.args) == 0:
die(0, "No files supplied (use --help for help)")
channel = self.options.channel
if not channel:
die(6, "Config channel not specified")
r = self.repository
if not r.config_channel_exists(channel):
die(6, "Error: config channel %s does not exist" % channel)
files = [utils.normalize_path(x) for x in self.args]
files_to_remove = []
if self.options.topdir:
if not os.path.isdir(self.options.topdir):
die(8, "--topdir specified, but `%s' not a directory" %
self.options.topdir)
for f in files:
if not f.startswith(self.options.topdir):
die(8, "--topdir %s specified, but file `%s' doesn't comply"
% (self.options.topdir, f))
files_to_remove.append((f, f[len(self.options.topdir):]))
else:
for f in files:
files_to_remove.append((f, f))
print("Removing from config channel %s" % channel)
for (local_file, remote_file) in files_to_remove:
try:
r.remove_file(channel, remote_file)
except xmlrpclib.Fault:
e = sys.exc_info()[1]
if e.faultCode == -4011:
print("%s does not exist" % remote_file)
continue
raise
else:
print("%s removed" % remote_file)
07070100000040000081B4000000000000000000000001625E8500000005EB000000000000000000000000000000000000003300000000mgr-cfg/config_management/rhncfg_remove_channel.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.
#
from config_common import handler_base, cfg_exceptions
from config_common.rhn_log import log_debug, die
class Handler(handler_base.HandlerBase):
_usage_options = "config_channel [ config_channel ... ]"
def run(self):
log_debug(2)
r = self.repository
if len(self.args) == 0:
die(5, "No config channels specified")
for config_channel in self.args:
print("Removing config channel %s" % config_channel)
try:
r.remove_config_channel(config_channel)
except cfg_exceptions.ConfigChannelNotInRepo:
die("Config channel %s does not exist" % config_channel)
except cfg_exceptions.ConfigChannelNotEmptyError:
die("Could not remove non-empty config channel %s" %
config_channel)
print("Config channel %s removed" % config_channel)
07070100000041000081B4000000000000000000000001625E850000000720000000000000000000000000000000000000002E00000000mgr-cfg/config_management/rhncfg_revisions.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.
#
from config_common import handler_base, cfg_exceptions
from config_common.rhn_log import log_debug, die
class Handler(handler_base.HandlerBase):
_usage_options = "[options] file [ file ... ]"
_options_table = handler_base.HandlerBase._options_table + [
handler_base.HandlerBase._option_class(
'-c', '--channel', action="store",
help="Use this config channel",
),
]
def run(self):
log_debug(2)
r = self.repository
channel = self.options.channel
if not channel:
die(6, "Config channel not specified")
if not self.args:
die(7, "No files specified")
print("Analyzing files in config channel %s" % channel)
for f in self.args:
if not r.has_file(channel, f):
die(8, "Config channel %s does not contain file %s" %
(channel, f))
try:
revisions = r.get_file_revisions(channel, f)
except cfg_exceptions.RepositoryFileMissingError:
print("%s: not in config channel" % f)
continue
print("%s: %s" % (f, " ".join([str(x) for x in revisions])))
07070100000042000081B4000000000000000000000001625E8500000002B4000000000000000000000000000000000000002B00000000mgr-cfg/config_management/rhncfg_update.py#
# Copyright (c) 2008--2013 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 rhncfg_add
class Handler(rhncfg_add.Handler):
is_first_revision=0
07070100000043000081B4000000000000000000000001625E8500000014CF000000000000000000000000000000000000003300000000mgr-cfg/config_management/rhncfg_upload_channel.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 os
from config_common import handler_base, utils, cfg_exceptions
from config_common.rhn_log import log_debug, log_error, die
class Handler(handler_base.HandlerBase):
_usage_options = "[options] [ config_channel ... ]"
_options_table = handler_base.HandlerBase._options_table + [
handler_base.HandlerBase._option_class(
'-t', '--topdir',
action="store",
default="./",
help="Directory all the file paths are relative to",
),
handler_base.HandlerBase._option_class(
'-c', '--channel',
action='store',
default=None,
help="List of channels the config info will be uploaded into. Channels delimited by ','.\nExample: --channel=foo,bar,baz",
),
handler_base.HandlerBase._option_class(
'', '--disable-selinux',
action='store_true',
default=None,
help="Don't upload SELinux contexts",
),
]
def run(self):
log_debug(2)
#5/12/05 wregglej - 149034 changed r into a instance variable
self.r = self.repository
topdir = self.options.topdir
if not topdir:
die(7, "--topdir not specified")
if not os.path.isdir(topdir):
die(8, "--topdir specified, but `%s' not a directory" %
topdir)
topdir = utils.normalize_path(topdir)
#5/12/05 wregglej - 149034 allowing the channel name and the directory name to vary independently.
if not self.options.channel is None:
#Get the list of channels with leading and trailing whitespace removed.
channels = [x.strip() for x in self.options.channel.split(',') if x]
#Get the list of directories to upload. At this point it's the list of arguments.
dirs = self.args
elif not self.args:
#If we get to this point, then --channel wasn't used and nothing was included as arguments.
#Assumes that the directories in topdir are the ones we want to upload, and since no channels were
#specified that each directory is it's own channel.
channels = os.listdir(topdir)
dirs = None
print("No config channels specified, using %s" % channels)
else:
#At this point, --channel wasn't used but there was something included as an argument.
#The name of the channel is assumed to be the same as the name of the directory.
channels = self.args
dirs = None
#If dirs isn't None, then each directory needs to be uploaded into each channel.
if dirs:
for channel in channels:
for directory in dirs:
self.upload_config_channel(topdir, channel, directory)
#If dirs is None, then each channel is it's own channel.
else:
for channel in channels:
self.upload_config_channel(topdir, channel, channel)
def upload_config_channel(self, topdir, channel, directory_name):
if not self.r.config_channel_exists(channel):
die(6, "Error: config channel %s does not exist" % channel)
if self.options.disable_selinux:
selinux_ctx = ''
else:
selinux_ctx = None
print("Using config channel %s" % channel)
channel_dir = utils.join_path(topdir, directory_name)
if not os.path.exists(channel_dir):
die(6, "Error: channel directory %s does not exist" % channel_dir)
flist = list_files_recursive(channel_dir)
for (dirname, filenames) in flist:
assert dirname.startswith(channel_dir)
remote_dirname = dirname[len(channel_dir):]
for f in filenames:
local_file = utils.join_path(dirname, f)
remote_file = utils.join_path(remote_dirname, f)
print("Uploading %s from %s" % (remote_file, local_file))
try:
self.r.put_file(channel, remote_file, local_file, is_first_revision=0,
selinux_ctx = selinux_ctx)
except cfg_exceptions.RepositoryFilePushError:
e = sys.exc_info()[1]
log_error(e)
def is_file_or_link(dirname, basename):
return os.path.isfile(os.path.join(dirname, basename)) or \
os.path.islink(os.path.join(dirname, basename))
def list_files_recursive(d):
file_list = []
for dirname, dirs, names in os.walk(d):
file_list.append((dirname, filter(lambda x, d=dirname: is_file_or_link(d, x),
names)))
return file_list
07070100000044000081B4000000000000000000000001625E850000003034000000000000000000000000000000000000002C00000000mgr-cfg/config_management/rpc_repository.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 os
import sys
import tempfile
from uyuni.common.usix import raise_with_tb
try: # python2
import xmlrpclib
except ImportError: # python3
import xmlrpc.client as xmlrpclib
from config_common import cfg_exceptions, repository, utils, file_utils
from config_common.rhn_log import log_debug
class Repository(repository.RPC_Repository):
_session_file = '.rhncfg-manager-session'
def __init__(self, setup_network=1):
log_debug(2)
repository.RPC_Repository.__init__(self, setup_network)
self.files_to_delete = []
self.username = None
self.password = None
self.session = None
def login(self, username=None, password=None):
self._get_session()
if not self.session and not (username and password):
raise cfg_exceptions.InvalidSession()
if self.session and not username:
test = self.rpc_call('config.test_session',
{'session' : self.session})
if not test:
raise cfg_exceptions.InvalidSession('Session is either expired or invalid')
else:
self.username = username
self.password = password
try:
self.session = self.rpc_call('config.rhn_login', {
'username' : self.username,
'password' : self.password,
})
except xmlrpclib.Fault:
e = sys.exc_info()[1]
fault_code, fault_string = e.faultCode, e.faultString
if fault_code == -2:
raise_with_tb(cfg_exceptions.AuthenticationError(
"Invalid username or incorrect password"), sys.exc_info()[2])
raise_with_tb(cfg_exceptions.InvalidSession(fault_code, fault_string), sys.exc_info()[2])
self._save_session()
if not self.session:
raise cfg_exceptions.InvalidSession
self.assert_repo_health()
def cleanup(self):
log_debug(4)
for file in self.files_to_delete:
if not os.path.isdir(file):
os.unlink(file)
def get_raw_file_info(self, config_channel, repopath, revision=None):
""" given a namepath, return the raw data passed by the server """
log_debug(5)
params = {
'session' : self.session,
'config_channel' : config_channel,
'path' : repopath,
}
if revision is not None:
params['revision'] = revision
try:
result = self.rpc_call('config.management.get_file', params)
except xmlrpclib.Fault:
e = sys.exc_info()[1]
if e.faultCode == -4011:
# File not present
raise_with_tb(cfg_exceptions.RepositoryFileMissingError(config_channel,
repopath), sys.exc_info()[2])
raise
return result
def get_file_info(self, config_channel, repopath, revision=None, auto_delete=1, dest_directory=tempfile.gettempdir()):
"""
given a namepath, return the filename and the rest of the info passed
by the server
"""
result = self.get_raw_file_info(config_channel, repopath, revision)
fp = file_utils.FileProcessor()
fullpath, dirs_created = fp.process(result, directory=dest_directory, strict_ownership=0)
if auto_delete:
self.files_to_delete.append(fullpath)
del result['file_contents']
return fullpath, result, dirs_created
def has_file(self, config_channel, repopath):
params = {
'session' : self.session,
'config_channel' : config_channel,
'path' : repopath,
}
return self.rpc_call('config.management.has_file', params)
def remove_file(self, config_channel, repopath):
""" remove a given file from the repo """
log_debug(4)
params = {
'session' : self.session,
'config_channel' : config_channel,
'path' : repopath,
}
return self.rpc_call('config.management.remove_file', params)
def put_file(self, config_channel, repopath, localfile=None,
is_first_revision=None, old_revision=None, delim_start=None,
delim_end=None, selinux_ctx=None):
"""
Insert a given file into the repo, overwriting if necessary.
localfile defaults to the repopath
"""
log_debug(4)
params = self._make_file_info(repopath, localfile,
delim_start=delim_start, delim_end=delim_end)
max_file_size = self.get_maximum_file_size()
if params['size'] > max_file_size:
error_msg = "%s too large (%s bytes, %s bytes max allowed)"
raise cfg_exceptions.ConfigFileTooLargeError(error_msg % (localfile, params['size'], max_file_size))
if selinux_ctx is not None:
params.update({
'selinux_ctx' : selinux_ctx,
})
params.update({
'session' : self.session,
'config_channel' : config_channel,
})
if is_first_revision:
params['is_first_revision'] = 1
elif old_revision:
params['old_revision'] = int(old_revision)
try:
result = self.rpc_call('config.management.put_file', params)
except xmlrpclib.Fault:
e = sys.exc_info()[1]
fault_code, fault_string = e.faultCode, e.faultString
if is_first_revision and fault_code == -4013:
raise_with_tb(cfg_exceptions.RepositoryFileExistsError(fault_string), sys.exc_info()[2])
if old_revision and fault_code == -4012:
raise_with_tb(cfg_exceptions.RepositoryFileVersionMismatchError(fault_string), sys.exc_info()[2])
if fault_code == -4003:
raise_with_tb(cfg_exceptions.ConfigFileTooLargeError(fault_string), sys.exc_info()[2])
if fault_code == -4014:
raise_with_tb(cfg_exceptions.QuotaExceeded(fault_string), sys.exc_info()[2])
raise_with_tb(cfg_exceptions.RepositoryFilePushError(fault_code, fault_string), sys.exc_info()[2])
return result
def config_channel_exists(self, config_channel):
log_debug(4, config_channel)
return (config_channel in self.list_config_channels())
def list_files(self, config_channel, repopath = None, recurse = 1):
"""
list files in a repo, recursing if requested;
repopath is not used yet
"""
log_debug(4)
files = self.rpc_call('config.management.list_files',
{'session' : self.session, 'config_channel' : config_channel})
return [p['path'] for p in files]
def get_file_revisions(self, config_channel, repopath):
"""
Fetch the file's revisions
"""
log_debug(4)
params = {
'session' : self.session,
'config_channel' : config_channel,
'path' : repopath,
}
try:
revisions = self.rpc_call('config.management.list_file_revisions',
params)
except xmlrpclib.Fault:
e = sys.exc_info()[1]
if e.faultCode == -4011:
# File not present
raise_with_tb(cfg_exceptions.RepositoryFileMissingError(
config_channel, repopath), sys.exc_info()[2])
raise
return revisions
def list_config_channels(self):
"List config channels"
log_debug(4)
if hasattr(self, 'config_channels'):
return self.config_channels
self.config_channels = self.rpc_call(
'config.management.list_config_channels', {'session' : self.session}
) or []
return self.config_channels
def create_config_channel(self, config_channel):
"creates a configuration channel"
log_debug(4, config_channel)
try:
return self.rpc_call('config.management.create_config_channel',
{'session' : self.session, 'config_channel' : config_channel})
except xmlrpclib.Fault:
e = sys.exc_info()[1]
if e.faultCode == -4010:
raise_with_tb(cfg_exceptions.ConfigChannelAlreadyExistsError(config_channel), sys.exc_info()[2])
raise
def remove_config_channel(self, config_channel):
"Removes a configuration channel"
log_debug(4, config_channel)
try:
return self.rpc_call('config.management.remove_config_channel',
{'session' : self.session, 'config_channel' : config_channel})
except xmlrpclib.Fault:
e = sys.exc_info()[1]
if e.faultCode == -4009:
raise_with_tb(cfg_exceptions.ConfigChannelNotInRepo(config_channel), sys.exc_info()[2])
if e.faultCode == -4005:
raise_with_tb(cfg_exceptions.ConfigChannelNotEmptyError(config_channel), sys.exc_info()[2])
raise
def _get_default_delimiters(self):
"retrieves the default delimiters from the server"
log_debug(4)
result = self.rpc_call('config.management.get_default_delimiters',
{'session' : self.session})
return result.get('delim_start'), result.get('delim_end')
def _get_maximum_file_size(self):
"get the maximum file size from the server"
log_debug(4)
result = self.rpc_call('config.management.get_maximum_file_size',
{'session' : self.session})
return result
def assert_repo_health(self):
log_debug(4)
pass
def diff_file_revisions(self, path, config_channel_src, revision_src,
config_channel_dst, revision_dst):
log_debug(4)
params = {
'session' : self.session,
'path' : path,
'config_channel_src': config_channel_src,
'revision_src' : revision_src,
}
if config_channel_dst is not None:
params['config_channel_dst'] = config_channel_dst
if revision_dst is not None:
params['revision_dst'] = revision_dst
try:
ret = self.rpc_call('config.management.diff', params)
except xmlrpclib.Fault:
e = sys.exc_info()[1]
if e.faultCode == -4011:
# File not present
raise_with_tb(cfg_exceptions.RepositoryFileMissingError(e.faultString), sys.exc_info()[2])
if e.faultCode == -4004:
# Binary file requested
raise_with_tb(cfg_exceptions.BinaryFileDiffError(e.faultString), sys.exc_info()[2])
raise
return ret
def _get_session(self):
session_path = self._get_session_path()
try:
fh = open(session_path, 'r')
self.session = fh.read()
fh.close()
except IOError:
# session file not there...
self.session = None
return self.session
def _save_session(self):
if not self.session:
self._remove_session()
session_path = self._get_session_path()
fh = open(session_path, "w+")
fh.write(self.session)
fh.close()
def _remove_session(self):
p = self._get_session_path()
try:
os.unlink(p)
except OSError:
pass
def _get_session_path(self):
return os.path.join(utils.get_home_dir(), self._session_file)
07070100000045000081B4000000000000000000000001625E85000000003C000000000000000000000000000000000000001A00000000mgr-cfg/mgr-cfg-rpmlintrcaddFilter("suse-filelist-forbidden-sysconfig .*/sysconfig")
07070100000046000081B4000000000000000000000001625E850000003C27000000000000000000000000000000000000001800000000mgr-cfg/mgr-cfg.changes-------------------------------------------------------------------
Tue Apr 19 11:46:31 CEST 2022 - jgonzalez@suse.com
- version 4.3.6-1
* Fix the condition for preventing building python 2 subpackage
for SLE15 (bsc#1197579)
-------------------------------------------------------------------
Fri Mar 11 14:43:33 CET 2022 - jgonzalez@suse.com
- version 4.3.5-1
* Corrected source URL in spec file.
-------------------------------------------------------------------
Wed Jan 26 11:48:40 CET 2022 - jgonzalez@suse.com
- version 4.3.4-1
* Fix installation problem for SLE15SP4 due missing python-selinux
-------------------------------------------------------------------
Tue Jan 18 13:30:50 CET 2022 - jgonzalez@suse.com
- version 4.3.3-1
* Fix python selinux package name depending on build target (bsc#1193600)
* Do not build python 2 package for SLE15SP4 and higher
-------------------------------------------------------------------
Fri Nov 05 13:33:47 CET 2021 - jgonzalez@suse.com
- version 4.3.2-1
* Remove unused legacy code
-------------------------------------------------------------------
Mon Aug 09 10:48:16 CEST 2021 - jgonzalez@suse.com
- version 4.3.1-1
- Bump version to 4.3.0
-------------------------------------------------------------------
Wed Jan 27 12:52:41 CET 2021 - jgonzalez@suse.com
- version 4.2.2-1
- SPEC: Updated Python definitions for RHEL8 and quoted text comparisons.
-------------------------------------------------------------------
Fri Sep 18 11:06:38 CEST 2020 - jgonzalez@suse.com
- version 4.2.1-1
- Update package version to 4.2.0
-------------------------------------------------------------------
Wed Mar 11 10:47:21 CET 2020 - jgonzalez@suse.com
- version 4.1.2-1
- Remove commented code in test files
-------------------------------------------------------------------
Wed Nov 27 16:55:00 CET 2019 - jgonzalez@suse.com
- version 4.1.1-1
- replace spacewalk-usix with uyuni-common-libs
- Bump version to 4.1.0 (bsc#1154940)
- Obsolete all old python2-rhncfg* packages to avoid conflicts (bsc#1152290)
- Fix data type issue to correctly decode if needed (bsc#1150320)
- Add mgr manpage links
- Require mgr-daemon (new name of spacewalksd) so we systems with
spacewalksd get always the new package installed (bsc#1149353)
-------------------------------------------------------------------
Wed Jul 31 17:25:43 CEST 2019 - jgonzalez@suse.com
- version 4.0.9-1
- Ensure bytes type when using hashlib to avoid traceback (bsc#1138822)
-------------------------------------------------------------------
Wed May 15 20:06:15 CEST 2019 - jgonzalez@suse.com
- version 4.0.8-1
- Obsolete newer versions of rhncfg
-------------------------------------------------------------------
Wed May 15 14:58:23 CEST 2019 - jgonzalez@suse.com
- version 4.0.7-1
- SPEC cleanup
- Fix wrong bugzilla entry at changelog
-------------------------------------------------------------------
Mon Apr 22 14:40:08 CEST 2019 - jgonzalez@suse.com
- version 4.0.6-1
- Fix build for non SUSE/openSUSE distributions
-------------------------------------------------------------------
Mon Apr 22 12:00:20 CEST 2019 - jgonzalez@suse.com
- version 4.0.5-1
- create client tools compat links also for non-SUSE systems
(bsc#1131408)
- Add makefile and configuration for pylint
-------------------------------------------------------------------
Wed Feb 27 12:57:48 CET 2019 - jgonzalez@suse.com
- version 4.0.4-1
- Sync with Spacewalk
- make filemod more readable
- 1665858 - diff expects string not bytes
-------------------------------------------------------------------
Mon Dec 17 14:31:58 CET 2018 - jgonzalez@suse.com
- version 4.0.3-1
- Fix python2 compilation on openSUSE
-------------------------------------------------------------------
Fri Oct 26 09:52:13 CEST 2018 - jgonzalez@suse.com
- version 4.0.2-1
- use rpm for debian packaging
-------------------------------------------------------------------
Fri Aug 10 16:00:11 CEST 2018 - jgonzalez@suse.com
- version 4.0.1-1
- Bump version to 4.0.0 (bsc#1104034)
- Rename package to mgr-cfg to allow version 4.0.0 (bsc#1104034)
- Fix copyright for the package specfile (bsc#1103696)
-------------------------------------------------------------------
Wed May 23 08:59:19 CEST 2018 - jgonzalez@suse.com
- version 5.10.122.2-1
- format the file mode in unified way(bsc#1093529)
-------------------------------------------------------------------
Mon Mar 26 08:36:31 CEST 2018 - jgonzalez@suse.com
- version 5.10.122.1-1
- Sync with upstream (bsc#1083294)
- Build python2 packages on SUSE systems
-------------------------------------------------------------------
Mon Mar 05 08:39:27 CET 2018 - jgonzalez@suse.com
- version 5.10.121.2-1
- remove empty clean section from spec (bsc#1083294)
-------------------------------------------------------------------
Fri Feb 23 10:43:55 CET 2018 - jgonzalez@suse.com
- version 5.10.121.1-1
- Sync with upstream
-------------------------------------------------------------------
Fri Feb 02 11:54:33 CET 2018 - jgonzalez@suse.com
- version 5.10.119.2-1
- Improve webui for comparing files (bsc#1076201)
-------------------------------------------------------------------
Wed Jan 17 11:10:05 CET 2018 - jgonzalez@suse.com
- version 5.10.119.1-1
- Fix update mechanism when updating the updateservice (bsc#1073619)
- Add --config option to rhncfg-manager and rhncfg-client
- Add better handling of interrupted system calls
-------------------------------------------------------------------
Fri Nov 10 16:22:55 CET 2017 - mc@suse.de
- version 5.10.117.1-1
- rhncfg: add missing dirs to filelist
-------------------------------------------------------------------
Thu Oct 26 17:14:23 CEST 2017 - mc@suse.de
- version 5.10.116.1-1
- move files into proper python2/python3 subpackages
- store output in the action file so partial output can arrive to server
- print different message if file does not exist
- print a name of file which does not exist during diff
- tell user which file differs
- add password config option to rhncfg-manager
- execute remote commands in clean environment
-------------------------------------------------------------------
Tue May 23 07:55:23 CEST 2017 - mc@suse.de
- version 5.10.103.2-1
- Add password config option to rhncfg-manager.
- require rhnlib version with i18n module (bsc#1038483)
-------------------------------------------------------------------
Wed May 03 16:43:52 CEST 2017 - michele.bologna@suse.com
- version 5.10.103.1-1
-------------------------------------------------------------------
Fri Mar 31 10:00:10 CEST 2017 - mc@suse.de
- version 5.10.102.2-1
- fix missing import in rhncfg
-------------------------------------------------------------------
Tue Mar 07 16:01:22 CET 2017 - mc@suse.de
- version 5.10.102.1-1
- Updated links to github in spec files
- Symlink target overwritten when the symlink is replaced by a file
managed by rhncfg-client
- require spacewalk-usix indead of spacewalk-backend-usix
-------------------------------------------------------------------
Wed Jan 11 15:26:05 CET 2017 - michele.bologna@suse.com
- version 5.10.100.1-1
- Version bump to 5.10.100
-------------------------------------------------------------------
Mon Mar 21 16:40:54 CET 2016 - mc@suse.de
- version 5.10.88.2-1
- fix file permissions (bsc#970550)
-------------------------------------------------------------------
Wed Mar 02 12:14:47 CET 2016 - mc@suse.de
- version 5.10.88.1-1
- fixing removing of temporary files during transaction rollback for
rhncfg-manager.
- fixing removing directories which rhncfg-manager didn't create.
-------------------------------------------------------------------
Mon Nov 30 11:16:55 CET 2015 - mc@suse.de
- version 5.10.87.1-1
- python 2.4 compatibility
- remove temporary files when exception occurs
-------------------------------------------------------------------
Wed Oct 07 15:51:28 CEST 2015 - mc@suse.de
- version 5.10.85.1-1
- make rhncfg support sha256 and use it by default
- fix for assigning all groups user belongs to running process
- show server modified time with rhncfg-client diff
-------------------------------------------------------------------
Tue Mar 31 14:51:34 CEST 2015 - mc@suse.de
- version 5.10.65.11-1
- fix config diff - Avoid addition of None and str (bsc#920897)
-------------------------------------------------------------------
Tue Feb 03 12:03:24 CET 2015 - mc@suse.de
- version 5.10.65.10-1
- Normalize path sooner
- Fix directory creation
- Getting rid of Tabs and trailing spaces
-------------------------------------------------------------------
Thu Dec 04 13:22:40 CET 2014 - mc@suse.de
- version 5.10.65.9-1
- fix compare config files by checking permissions on the
correct file (bsc#900498)
-------------------------------------------------------------------
Fri Nov 07 13:25:00 CET 2014 - mc@suse.de
- version 5.10.65.8-1
- fix errror in rhncfg if selinux off
-------------------------------------------------------------------
Fri Sep 12 16:06:20 CEST 2014 - mc@suse.de
- version 5.10.65.7-1
- validate the content of config files before deploying
-------------------------------------------------------------------
Wed Sep 3 01:41:38 CEST 2014 - ro@suse.de
- sanitize release line in specfile
-------------------------------------------------------------------
Mon Sep 01 17:06:59 CEST 2014 - mc@suse.de
- version 5.10.65.6-1
- Avoid traceback with a configfiles upload action with no selinux context
- make webui config dir diff work
- ensure webui config file diff looks at owner and permissions
- make sure webui doesn't say there are diffs if there aren't
-------------------------------------------------------------------
Tue Jun 17 10:15:27 CEST 2014 - jrenner@suse.de
- version 5.10.65.5-1
- list/elist: allow to specify list of files
-------------------------------------------------------------------
Tue May 06 15:23:18 CEST 2014 - mc@suse.de
- version 5.10.65.4-1
- fix for assigning all groups user belongs to running process
-------------------------------------------------------------------
Thu Apr 10 17:29:31 CEST 2014 - mc@suse.de
- version 5.10.65.3-1
- fix uncaught exception in config deploy (bnc#871549)
-------------------------------------------------------------------
Thu Apr 10 17:29:01 CEST 2014 - mc@suse.de
- fix release in specfile for SLE12 (bnc#872970)
-------------------------------------------------------------------
Thu Mar 27 10:07:15 CET 2014 - fcastelli@suse.com
- version 5.10.65.2-1
- show server modified time with rhncfg-client diff
-------------------------------------------------------------------
Mon Dec 09 16:17:50 CET 2013 - mc@suse.de
- version 5.10.65.1-1
- switch to 2.1
-------------------------------------------------------------------
Wed Aug 21 15:46:06 CEST 2013 - mc@suse.de
- version 5.10.27.12-1
- rhncfg-* --server-name now overwrites rhncfg-*.conf
- unicode support for Remote Command scripts
-------------------------------------------------------------------
Wed Jun 12 13:41:14 CEST 2013 - mc@suse.de
- version 5.10.27.11-1
- make diffs initiated from another spacewalk server obey
display_diff config option
-------------------------------------------------------------------
Mon Apr 22 15:54:45 CEST 2013 - mc@suse.de
- version 5.10.27.10-1
- simplify rhncfg API (bnc#815460)
-------------------------------------------------------------------
Fri Feb 08 11:06:39 CET 2013 - mc@suse.de
- version 5.10.27.9-1
- Fix Web UI config diff, which always shows 'binary files differ'
- make rhncfg diff output configurable
-------------------------------------------------------------------
Mon Oct 15 12:21:31 CEST 2012 - mc@suse.de
- version 5.10.27.8-1
- recompile python files only on SUSE
-------------------------------------------------------------------
Fri Sep 28 15:57:46 CEST 2012 - mc@suse.de
- version 5.10.27.7-1
- precompile python code to prevent verification issues (bnc#776356)
- If there's symlink in file deployment path it will be created
-------------------------------------------------------------------
Mon Jul 16 15:16:08 CEST 2012 - ug@suse.de
- version 5.10.27.6-1
- check symlink not target file existence
- value of selinux context is important
-------------------------------------------------------------------
Thu Jun 21 11:16:28 CEST 2012 - jrenner@suse.de
- version 5.10.27.5-1
-------------------------------------------------------------------
Sun Jun 17 18:07:24 CEST 2012 - mc@suse.de
- fixed insecure permissions used for /var/log/rhncfg-actions file
CVE-2012-2679 (bnc#766148)
-------------------------------------------------------------------
Thu May 31 10:52:19 CEST 2012 - mc@suse.de
- version 5.10.27.4-1
- fixed command synopsis
-------------------------------------------------------------------
Mon May 14 10:49:40 CEST 2012 - mc@suse.de
- version 5.10.27.3-1
- honor rhncfg-specific settings
-------------------------------------------------------------------
Fri Apr 27 16:29:34 CEST 2012 - mc@suse.de
- version 5.10.27.2-1
- accept server name without protocol
-------------------------------------------------------------------
Wed Mar 21 18:00:07 CET 2012 - mc@suse.de
- version 5.10.27.1-1
- Bumping package version
-------------------------------------------------------------------
Thu Nov 3 19:25:06 CET 2011 - ma@suse.de
- Adapt dependencies to renamed packages (bnc#722052)
-------------------------------------------------------------------
Thu Sep 15 17:02:28 CEST 2011 - mc@suse.de
- do not require selinux on SLES10
-------------------------------------------------------------------
Mon May 16 15:00:51 CEST 2011 - iartarisi@suse.cz
- fix staticmethod Syntax Error on systems with python < 2.4 (e.g. RHEL4) (bnc#694028)
-------------------------------------------------------------------
Mon May 16 09:14:56 CEST 2011 - iartarisi@suse.cz
- fix usage documentation messages for topdir and dest-file (bnc#684390)
-------------------------------------------------------------------
Fri Mar 18 13:00:20 CET 2011 - iartarisi@suse.cz
- pulled a few commits from the master branch to fix bnc#679716 and a
more general problem we had with uploading config files without a
SELinux context
-------------------------------------------------------------------
Thu Mar 17 15:35:32 CET 2011 - mc@suse.de
- debrand rhncfg-manager (bnc#678489)
-------------------------------------------------------------------
Sun Jan 30 15:27:43 CET 2011 - mc@suse.de
- backport upstrem fixes
-------------------------------------------------------------------
Mon Jan 17 13:44:41 CET 2011 - mantel@suse.de
- add mgr-* symlinks (BNC #660789)
-------------------------------------------------------------------
Wed Nov 3 09:49:28 CET 2010 - mantel@suse.de
- fix %dir in filelist
-------------------------------------------------------------------
Tue Nov 2 17:30:40 CET 2010 - mantel@suse.de
- don't ignore post-build-checks
-------------------------------------------------------------------
Wed Sep 15 12:13:17 CEST 2010 - mantel@suse.de
- fix post-build-checks
-------------------------------------------------------------------
Tue Sep 14 17:47:16 CEST 2010 - mantel@suse.de
- Initial release of rhncfg
-------------------------------------------------------------------
07070100000047000081B4000000000000000000000001625E850000003E5B000000000000000000000000000000000000001500000000mgr-cfg/mgr-cfg.spec#
# spec file for package mgr-cfg
#
# Copyright (c) 2021 SUSE LLC
# 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/
#
# package renaming fun :(
%define rhn_client_tools spacewalk-client-tools
%define rhn_setup spacewalk-client-setup
%define rhn_check spacewalk-check
%define rhnsd mgr-daemon
# Old name and version+1 before renaming to mgr-cfg
%define oldname rhncfg
%define oldversion 5.10.123
#
%global rhnroot %{_datadir}/rhn
%global rhnconf %{_sysconfdir}/sysconfig/rhn
%global client_caps_dir %{rhnconf}/clientCaps.d
%if 0%{?fedora} || 0%{?suse_version} > 1320 || 0%{?rhel} >= 8
%global build_py3 1
%global default_py3 1
%global __python /usr/bin/python2
%endif
%if ( 0%{?fedora} && 0%{?fedora} < 28 ) || ( 0%{?rhel} && 0%{?rhel} < 8 ) || (0%{?suse_version} && 0%{?sle_version} < 150000) || 0%{?ubuntu} || 0%{?debian}
%global build_py2 1
%endif
# ------------------------------- Python macros for debian ----------------------------------------
%{!?__python2:%global __python2 /usr/bin/python2}
%{!?__python3:%global __python3 /usr/bin/python3}
%if %{undefined python2_version}
%global python2_version %(%{__python2} -Esc "import sys; sys.stdout.write('{0.major}.{0.minor}'.format(sys.version_info))")
%endif
%if %{undefined python3_version}
%global python3_version %(%{__python3} -Ic "import sys; sys.stdout.write(sys.version[:3])")
%endif
%if %{undefined python2_sitelib}
%global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
%endif
%if %{undefined python3_sitelib}
%global python3_sitelib %(%{__python3} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
%endif
# --------------------------- End Python macros for debian ----------------------------------------
%define pythonX %{?default_py3: python3}%{!?default_py3: python2}
%if "%{_vendor}" == "debbuild"
# Bash constructs in scriptlets don't play nice with Debian's default shell, dash
%global _buildshell /bin/bash
%endif
Name: mgr-cfg
Version: 4.3.6
Release: 1
Provides: %{oldname} = %{oldversion}
Obsoletes: %{oldname} < %{oldversion}
Summary: Spacewalk Configuration Client Libraries
License: GPL-2.0-only
%if "%{_vendor}" == "debbuild"
Group: admin
Packager: Uyuni Project <uyuni-devel@opensuse.org>
%else
Group: Applications/System
%endif
URL: https://github.com/uyuni-project/uyuni
Source0: https://github.com/uyuni-project/uyuni/archive/%{name}-%{version}-1.tar.gz
Source1: https://raw.githubusercontent.com/uyuni-project/uyuni/%{name}-%{version}-1/client/tools/mgr-cfg/%{name}-rpmlintrc
BuildRoot: %{_tmppath}/%{name}-%{version}-build
%if 0%{?fedora} || 0%{?rhel} || 0%{?suse_version} >= 1210
BuildArch: noarch
%endif
BuildRequires: docbook-utils
Requires: %{pythonX}-%{name} = %{version}-%{release}
%if "%{_vendor}" != "debbuild"
%if 0%{?suse_version}
# provide rhn directories and no selinux on suse
BuildRequires: spacewalk-client-tools
%if 0%{?suse_version} >= 1110 && 0%{?suse_version} < 1500
Requires: python-selinux
%endif
%if 0%{?suse_version} >= 1500
Requires: python3-selinux
%endif
%else
Requires: libselinux-python
%endif
%endif
%if "%{_vendor}" == "debbuild"
%if 0%{?build_py2}
Requires: python-selinux
%endif
%if 0%{?build_py3}
Requires: python3-selinux
%endif
%endif
%description
The base libraries and functions needed by all mgr-cfg-* packages.
%if 0%{?build_py2}
%package -n python2-%{name}
Summary: Spacewalk Configuration Client Libraries
Group: Applications/System
Provides: python-%{name} = %{oldversion}
Obsoletes: python-%{name} < %{oldversion}
Provides: python2-%{oldname} = %{oldversion}
Obsoletes: python2-%{oldname} < %{oldversion}
Requires: %{name} = %{version}-%{release}
Requires: python2-rhn-client-tools >= 2.8.4
Requires: python2-uyuni-common-libs
Requires: rhnlib >= 2.8.3
%if 0%{?rhel} && 0%{?rhel} <= 5
Requires: python-hashlib
%endif
%if 0%{?rhel} >= 8
Requires: python2
BuildRequires: python2
%else
Requires: python < 3
BuildRequires: python < 3
%endif
%if "%{_vendor}" == "debbuild"
# For scriptlets
Requires(preun): python-minimal
Requires(post): python-minimal
%endif
%description -n python2-%{name}
Python 2 specific files for %{name}.
%endif
%if 0%{?build_py3}
%package -n python3-%{name}
Summary: Spacewalk Configuration Client Libraries
Group: Applications/System
Requires: %{name} = %{version}-%{release}
Provides: python3-%{oldname} = %{oldversion}
Obsoletes: python3-%{oldname} < %{oldversion}
Requires: python3
Requires: python3-rhn-client-tools >= 2.8.4
Requires: python3-rhnlib >= 2.8.3
Requires: python3-uyuni-common-libs
BuildRequires: python3
%if "%{_vendor}" != "debbuild"
BuildRequires: python3-rpm-macros
%endif
%if "%{_vendor}" == "debbuild"
# For scriptlets
Requires(preun): python3-minimal
Requires(post): python3-minimal
%endif
%description -n python3-%{name}
Python 3 specific files for %{name}.
%endif
%package client
Summary: Spacewalk Configuration Client
Group: Applications/System
Provides: %{oldname}-client = %{oldversion}
Obsoletes: %{oldname}-client < %{oldversion}
Requires: %{name} = %{version}-%{release}
Requires: %{pythonX}-%{name}-client = %{version}-%{release}
%description client
A command line interface to the client features of the Spacewalk Configuration
Management system.
%if 0%{?build_py2}
%package -n python2-%{name}-client
Summary: Spacewalk Configuration Client
Group: Applications/System
Provides: python-%{name}-client = %{oldversion}
Obsoletes: python-%{name}-client < %{oldversion}
Provides: python2-%{oldname}-client = %{oldversion}
Obsoletes: python2-%{oldname}-client < %{oldversion}
Requires: %{name}-client = %{version}-%{release}
%if "%{_vendor}" == "debbuild"
# For scriptlets
Requires(preun): python-minimal
Requires(post): python-minimal
%endif
%description -n python2-%{name}-client
Python 2 specific files for %{name}-client.
%endif
%if 0%{?build_py3}
%package -n python3-%{name}-client
Summary: Spacewalk Configuration Client
Group: Applications/System
Provides: python3-%{oldname}-client = %{oldversion}
Obsoletes: python3-%{oldname}-client < %{oldversion}
Requires: %{name}-client = %{version}-%{release}
%if "%{_vendor}" == "debbuild"
# For scriptlets
Requires(preun): python3-minimal
Requires(post): python3-minimal
%endif
%description -n python3-%{name}-client
Python 3 specific files for %{name}-client.
%endif
%package management
Summary: Spacewalk Configuration Management Client
Group: Applications/System
Provides: %{oldname}-management = %{oldversion}
Obsoletes: %{oldname}-management < %{oldversion}
Requires: %{name} = %{version}-%{release}
Requires: %{pythonX}-%{name}-management = %{version}-%{release}
%description management
A command line interface used to manage Spacewalk configuration.
%if 0%{?build_py2}
%package -n python2-%{name}-management
Summary: Spacewalk Configuration Management Client
Group: Applications/System
Provides: python-%{name}-management = %{oldversion}
Obsoletes: python-%{name}-management < %{oldversion}
Provides: python2-%{oldname}-management = %{oldversion}
Obsoletes: python2-%{oldname}-management < %{oldversion}
Requires: %{name}-management = %{version}-%{release}
%if "%{_vendor}" == "debbuild"
# For scriptlets
Requires(preun): python-minimal
Requires(post): python-minimal
%endif
%description -n python2-%{name}-management
Python 2 specific files for python2-%{name}-management.
%endif
%if 0%{?build_py3}
%package -n python3-%{name}-management
Summary: Spacewalk Configuration Management Client
Group: Applications/System
Provides: python3-%{oldname}-management = %{oldversion}
Obsoletes: python3-%{oldname}-management < %{oldversion}
Requires: %{name}-management = %{version}-%{release}
%if "%{_vendor}" == "debbuild"
# For scriptlets
Requires(preun): python3-minimal
Requires(post): python3-minimal
%endif
%description -n python3-%{name}-management
Python 2 specific files for python3-%{name}-management.
%endif
%package actions
Summary: Spacewalk Configuration Client Actions
Group: Applications/System
Provides: %{oldname}-actions = %{oldversion}
Obsoletes: %{oldname}-actions < %{oldversion}
Requires: %{name} = %{version}-%{release}
Requires: %{pythonX}-%{name}-actions = %{version}-%{release}
%description actions
The code required to run configuration actions scheduled via Spacewalk.
%if 0%{?build_py2}
%package -n python2-%{name}-actions
Summary: Spacewalk Configuration Client Actions
Group: Applications/System
Provides: python-%{name}-actions = %{oldversion}
Obsoletes: python-%{name}-actions < %{oldversion}
Provides: python2-%{oldname}-actions = %{oldversion}
Obsoletes: python2-%{oldname}-actions < %{oldversion}
Requires: %{name}-actions = %{version}-%{release}
Requires: python2-%{name}-client
%if "%{_vendor}" == "debbuild"
# For scriptlets
Requires(preun): python-minimal
Requires(post): python-minimal
%endif
%description -n python2-%{name}-actions
Python 2 specific files for python2-%{name}-actions.
%endif
%if 0%{?build_py3}
%package -n python3-%{name}-actions
Summary: Spacewalk Configuration Client Actions
Group: Applications/System
Provides: python3-%{oldname}-actions = %{oldversion}
Obsoletes: python3-%{oldname}-actions < %{oldversion}
Requires: %{name}-actions = %{version}-%{release}
Requires: python3-%{name}-client
%if "%{_vendor}" == "debbuild"
# For scriptlets
Requires(preun): python3-minimal
Requires(post): python3-minimal
%endif
%description -n python3-%{name}-actions
Python 3 specific files for python2-%{name}-actions.
%endif
%prep
%setup -q
%build
make -f Makefile.rhncfg all
%install
install -d $RPM_BUILD_ROOT/%{python_sitelib}
%if 0%{?build_py2}
make -f Makefile.rhncfg install PREFIX=$RPM_BUILD_ROOT ROOT=%{python_sitelib} \
MANDIR=%{_mandir} PYTHONVERSION=%{python_version}
%endif
%if 0%{?build_py3}
install -d $RPM_BUILD_ROOT/%{python3_sitelib}
sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|' config_*/*.py actions/*.py
make -f Makefile.rhncfg install PREFIX=$RPM_BUILD_ROOT ROOT=%{python3_sitelib} \
MANDIR=%{_mandir} PYTHONVERSION=%{python3_version}
%endif
mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/spool/rhn
mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/log
touch $RPM_BUILD_ROOT/%{_localstatedir}/log/rhncfg-actions
# create links to default script version
%define default_suffix %{?default_py3:-%{python3_version}}%{!?default_py3:-%{python_version}}
for i in \
/usr/bin/rhncfg-client \
/usr/bin/rhncfg-manager \
/usr/bin/rhn-actions-control \
; do
ln -s $(basename "$i")%{default_suffix} "$RPM_BUILD_ROOT$i"
done
ln -s rhncfg-manager $RPM_BUILD_ROOT/%{_bindir}/mgrcfg-manager
ln -s rhncfg-client $RPM_BUILD_ROOT/%{_bindir}/mgrcfg-client
ln -s rhn-actions-control $RPM_BUILD_ROOT/%{_bindir}/mgr-actions-control
pushd $RPM_BUILD_ROOT/%{_mandir}/man8
for f in *.8*; do
echo ".so ${f/.gz}" | gzip > ${f/rhn/mgr}
done
popd
%if 0%{?suse_version}
%if 0%{?build_py2}
%py_compile -O %{buildroot}/%{python_sitelib}
%endif
%if 0%{?build_py3}
%py3_compile -O %{buildroot}/%{python3_sitelib}
%endif
%endif
%post
if [ -f %{_localstatedir}/log/rhncfg-actions ]
then
chown root %{_localstatedir}/log/rhncfg-actions
chmod 600 %{_localstatedir}/log/rhncfg-actions
fi
%if "%{_vendor}" == "debbuild"
# Debian requires:
# post: Do bytecompilation after install
# preun: Remove any *.py[co] files
%if 0%{?build_py2}
%post -n python2-%{name}
pycompile python2-%{name} -V -3.0
%preun -n python2-%{name}
pyclean -p python2-%{name}
%post -n python2-%{name}-client
pycompile python2-%{name}-client -V -3.0
%preun -n python2-%{name}-client
pyclean -p python2-%{name}-client
%post -n python2-%{name}-management
pycompile python2-%{name}-management -V -3.0
%preun -n python2-%{name}-management
pyclean -p python2-%{name}-management
%post -n python2-%{name}-actions
pycompile python2-%{name}-actions -V -3.0
%preun -n python2-%{name}-actions
pyclean -p python2-%{name}-actions
%endif
%if 0%{?build_py3}
%post -n python3-%{name}
py3compile python3-%{name} -V -4.0
%preun -n python3-%{name}
py3clean -p python3-%{name}
%post -n python3-%{name}-client
py3compile python3-%{name}-client -V -4.0
%preun -n python3-%{name}-client
py3clean -p python3-%{name}-client
%post -n python3-%{name}-management
py3compile python3-%{name}-management -V -4.0
%preun -n python3-%{name}-management
py3clean -p python3-%{name}-management
%post -n python3-%{name}-actions
py3compile python3-%{name}-actions -V -4.0
%preun -n python3-%{name}-actions
py3clean -p python3-%{name}-actions
%endif
%endif
%files
%defattr(-,root,root,-)
%dir %{_localstatedir}/spool/rhn
%{!?_licensedir:%global license %doc}
%license LICENSE
%if 0%{?build_py2}
%files -n python2-%{name}
%defattr(-,root,root,-)
%{python_sitelib}/config_common
%endif
%if 0%{?build_py3}
%files -n python3-%{name}
%defattr(-,root,root,-)
%{python3_sitelib}/config_common
%endif
%files client
%defattr(-,root,root,-)
%{_bindir}/rhncfg-client
%{_bindir}/mgrcfg-client
%attr(644,root,root) %config(noreplace) %{rhnconf}/rhncfg-client.conf
%{_mandir}/man8/rhncfg-client.8*
%{_mandir}/man8/mgrcfg-client.8*
%if 0%{?build_py2}
%files -n python2-%{name}-client
%defattr(-,root,root,-)
%{python_sitelib}/config_client
%{_bindir}/rhncfg-client-%{python_version}
%endif
%if 0%{?build_py3}
%files -n python3-%{name}-client
%defattr(-,root,root,-)
%{python3_sitelib}/config_client
%{_bindir}/rhncfg-client-%{python3_version}
%endif
%files management
%defattr(-,root,root,-)
%{_bindir}/mgrcfg-manager
%{_bindir}/rhncfg-manager
%attr(644,root,root) %config(noreplace) %{rhnconf}/rhncfg-manager.conf
%{_mandir}/man8/rhncfg-manager.8*
%{_mandir}/man8/mgrcfg-manager.8*
%if 0%{?build_py2}
%files -n python2-%{name}-management
%defattr(-,root,root,-)
%{python_sitelib}/config_management
%{_bindir}/rhncfg-manager-%{python_version}
%endif
%if 0%{?build_py3}
%files -n python3-%{name}-management
%defattr(-,root,root,-)
%{python3_sitelib}/config_management
%{_bindir}/rhncfg-manager-%{python3_version}
%endif
%files actions
%defattr(-,root,root,-)
%{_bindir}/mgr-actions-control
%{_bindir}/rhn-actions-control
%config(noreplace) %{client_caps_dir}/*
%{_mandir}/man8/rhn-actions-control.8*
%{_mandir}/man8/mgr-actions-control.8*
%ghost %attr(600,root,root) %{_localstatedir}/log/rhncfg-actions
%if 0%{?build_py2}
%files -n python2-%{name}-actions
%defattr(-,root,root,-)
%dir %{python_sitelib}/rhn
%{python_sitelib}/rhn/actions
%{_bindir}/rhn-actions-control-%{python_version}
%if 0%{?suse_version}
%dir %{python_sitelib}/rhn
%endif
%endif
%if 0%{?build_py3}
%files -n python3-%{name}-actions
%defattr(-,root,root,-)
%dir %{python3_sitelib}/rhn
%{python3_sitelib}/rhn/actions
%{_bindir}/rhn-actions-control-%{python3_version}
%if 0%{?suse_version}
%dir %{python3_sitelib}/rhn
%endif
%endif
%changelog
07070100000048000081B4000000000000000000000001625E850000001395000000000000000000000000000000000000001100000000mgr-cfg/pylintrc# mgr-cfg 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=
07070100000049000041FD000000000000000000000001625E850000000000000000000000000000000000000000000000001000000000mgr-cfg/solaris0707010000004A000081FD000000000000000000000001625E85000000073B000000000000000000000000000000000000002100000000mgr-cfg/solaris/build-solaris.sh#!/usr/bin/bash
# clean up junk...
make clean
rm Prototype
rm -rf /tmp/RHATrncfg*
rm -rf /test-bretm/build-target
# where you want stuff to land
DEST_PREFIX=/opt/redhat/rhn/solaris
export DEST_PREFIX
# think of this as the buildroot, should be some temp dir
BUILD_ROOT=/test-bretm/build-target
export BUILD_ROOT
# PREFIX used by the makefiles...
PREFIX="$BUILD_ROOT$DEST_PREFIX"
export PREFIX
INSTALL='/usr/ucb/install -c'
export INSTALL
LN='ln -sf'
export LN
rm -rf temp_src
mkdir temp_src
cp * temp_src/
cp -R actions temp_src/
cp -R config_* temp_src/
cd temp_src
perl -pi -e "s|/usr/bin/diff|$DEST_PREFIX/bin/diff|" \
./config_client/rhncfgcli_diff.py \
./config_common/file_utils.py \
./config_management/rhncfg_diff.py
perl -pi -e "s|/usr/share/rhn|$DEST_PREFIX/usr/share/rhn|" \
./config_client/* \
./config_management/*
perl -pi -e "s|/etc/sysconfig/rhn/allowed-actions|$DEST_PREFIX/etc/sysconfig/rhn/allowed-actions|" \
./actions/configfiles.py
perl -pi -e "s|/etc/sysconfig/rhn/systemid|$DEST_PREFIX/etc/sysconfig/rhn/systemid|" \
./config_client/*
perl -pi -e "s|/etc/sysconfig/rhn/up2date|$DEST_PREFIX/etc/sysconfig/rhn/up2date|" \
./config_common/up2date_config_parser.py
perl -pi -e "s|os.path.join\(os.sep|os.path.join\('$DEST_PREFIX'|" \
./config_common/local_config.py
perl -pi -e "s|^#!/usr/bin/python|#!$DEST_PREFIX/bin/python|" \
./config_client/rhncfg-client.py \
./config_management/rhncfg-manager.py
make
make install
echo "i pkginfo" > Prototype
#echo "i postinstall" >> Prototype
#echo "i checkinstall" >> Prototype
find $PREFIX -print | pkgproto | sed "s#$PREFIX#$DEST_PREFIX#g" >> Prototype
pkgmk -o -r $BUILD_ROOT -d /tmp -f Prototype
cd /tmp
# tar -cf - RHATrncfg | gzip -9 -c > RHATrncfg-1.0.sparc.pkg.tar.gz
pkgtrans -s /tmp RHATrncfg-1.0.pkg RHATrncfg
0707010000004B000081B4000000000000000000000001625E8500000000A6000000000000000000000000000000000000001800000000mgr-cfg/solaris/pkginfoPKG="RHATrncfg"
NAME="Spacewalk configuration client"
VERSION="1.0"
ARCH="sparc"
CLASSES="none"
CATEGORY="utility"
VENDOR="RHAT"
EMAIL="bretm@redhat.com"
BASEDIR="/"
0707010000004C000041FD000000000000000000000001625E850000000000000000000000000000000000000000000000000D00000000mgr-cfg/test0707010000004D000081B4000000000000000000000001625E8500000009D9000000000000000000000000000000000000002300000000mgr-cfg/test/test_action_deploy.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 os
import fnmatch
from actions import configfiles
from config_common import local_config, rhn_log
local_config.init("rhncfg")
rhn_log.set_debug_level(local_config.get('debug'))
def test(msg, test):
ok_str = 'NOT OK'
if test:
ok_str = 'ok'
print("%s: %s" % (msg, ok_str))
def stray_files(path):
(directory, filename) = os.path.split(path)
for path in os.listdir(directory):
if fnmatch.fnmatch(path, "%s*" % filename):
return 1
return 0
files = { 'files' : [
{
'path' : "/etc/googah",
'namespace' : "foo1",
'file_contents' : "This is 1 and this is 2\nAnd this is 3",
'md5sum' : "0abcabcabcabc",
'delim_start' : "[|",
'delim_end' : "|]",
},
{
'path' : "/var/tmp/ggg",
'namespace' : "foo1",
'file_contents' : "That is 1 and this is 2\nAnd this is 3",
'md5sum' : "0abcabcabcabc",
'delim_start' : "[|",
'delim_end' : "|]",
},
]}
try:
for file in files['files']:
os.unlink(file['path'])
except OSError:
pass
test("testing a clean new deployment",
configfiles.deploy(files)[0] == 0)
file = files['files'][0]['path']
os.system("chattr +i %s" % file)
test("testing deployment over read-only file (no files deployed, remove backups)",
configfiles.deploy(files)[0] == 43)
os.system("chattr -i %s" % file)
test("testing redeployment w/ backups",
configfiles.deploy(files)[0] == 0)
file = files['files'][1]['path']
os.system("chattr +i %s" % file)
test("testing deployment over read-only file (partial deployment, restore from then remove backups)",
configfiles.deploy(files)[0] == 43 and os.path.exists(file))
os.system("chattr -i %s" % file)
test("testing redeployment w/ backups (again)",
configfiles.deploy(files)[0] == 0)
0707010000004E000081B4000000000000000000000001625E8500000005D8000000000000000000000000000000000000002100000000mgr-cfg/test/test_action_diff.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.
#
from actions import configfiles
print(configfiles.diff([
{
'path' : "/etc/googah",
'namespace' : "foo1",
'file_contents' : "This is 1 and this is 2\nAnd this is 3",
'md5sum' : "0abcabcabcabc",
'delim_start' : "[|",
'delim_end' : "|]",
},
{
'path' : "/var/tmp/ggg",
'namespace' : "foo1",
'file_contents' : "That is 1 and this is 2\nAnd this is 3",
'md5sum' : "0abcabcabcabc",
'delim_start' : "[|",
'delim_end' : "|]",
},
{
'path' : "/etc/issue",
'namespace' : "foo1",
'file_contents' : "Red Hat Linux release 7.3 Evaluation (Valhalla)\nKernel \\r on an \\m",
'md5sum' : "0abcabcabcabc",
'delim_start' : "[|",
'delim_end' : "|]",
},
]))
0707010000004F000081B4000000000000000000000001625E850000000392000000000000000000000000000000000000002300000000mgr-cfg/test/test_action_script.py#!/usr/bin/python
#
# 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.
#
from actions import script
print(script.run(1, {
'username' : 'misa',
'groupname' : 'misa',
'now' : '2004-02-20 09:00:00',
'script' : """\
#!/usr/bin/python
import sys
import time
sys.stdout.write("1\\n")
sys.stderr.write("2\\n")
time.sleep(10)
""",
}))
07070100000050000081B4000000000000000000000001625E8500000002EE000000000000000000000000000000000000002300000000mgr-cfg/test/test_action_upload.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.
#
from actions import configfiles
from config_common import local_config
local_config.init('rhncfgcli')
print(configfiles.upload("foo"))
07070100000051000081B4000000000000000000000001625E8500000003D0000000000000000000000000000000000000001E00000000mgr-cfg/test/test_maketemp.py#!/usr/bin/python
#
# 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 os
from config_common import file_utils
filepath, fd = file_utils.maketemp("/tmp/my-file-", mode=int("0700", 8))
buf = "0123456789" * 100
print("Writing", len(buf), "to", filepath)
os.write(fd, buf)
assert(len(buf) == f.tell())
os.close(fd)
os.unlink(filepath)
filepath, fd = file_utils.maketemp()
os.close(fd)
os.unlink(filepath)
07070100000052000081B4000000000000000000000001625E8500000006A8000000000000000000000000000000000000001C00000000mgr-cfg/test/test_upload.py#!/usr/bin/python
#
# 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.
#
from rhn import rpclib
systemid_file = '/home/devel/misa/cvs/rhn/test/backend/checks/systemid-farm06'
server = "coyote.devel.redhat.com"
#server = "rhnxml.back-webdev.redhat.com"
s = rpclib.Server("http://%s/CONFIG-MANAGEMENT" % server)
files = [
{
'path' : '/etc/motd',
'file_contents' : 'This system will not work today\nCause I like it so\n',
'delim_start' : '{|',
'delim_end' : '|}',
'file_stat' : {
'size' : 1234,
'mode' : int("0755", 8),
'user' : 'misa',
'group' : 'misa',
},
},
{
'path' : '/etc/voodoo',
'file_contents' : 'If you read this file, your computer will reboot',
'delim_start' : '{|',
'delim_end' : '|}',
'file_stat' : {
'size' : 1234,
'mode' : int("0755", 8),
'user' : 'misa',
'group' : 'misa',
},
},
]
systemid = open(systemid_file).read()
s.config.client.upload_files(systemid, 11921200, files)
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!