Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15:Update
koan
koan-3.0.1.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File koan-3.0.1.obscpio of Package koan
07070100000000000081A400000000000000000000000160FFEB8C00000E27000000000000000000000000000000000000001900000000koan-3.0.1/.dockerignore### Python template # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ ### JetBrains template # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf # Generated files .idea/**/contentModel.xml # Sensitive or high-churn files .idea/**/dataSources/ .idea/**/dataSources.ids .idea/**/dataSources.local.xml .idea/**/sqlDataSources.xml .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml # Gradle .idea/**/gradle.xml .idea/**/libraries # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. # .idea/artifacts # .idea/compiler.xml # .idea/jarRepositories.xml # .idea/modules.xml # .idea/*.iml # .idea/modules # *.iml # *.ipr # CMake cmake-build-*/ # Mongo Explorer plugin .idea/**/mongoSettings.xml # File-based project format *.iws # IntelliJ out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Cursive Clojure plugin .idea/replstate.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties # Editor-based Rest Client .idea/httpRequests # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser # User stuff rpm-build deb-build 07070100000001000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001300000000koan-3.0.1/.github07070100000002000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001D00000000koan-3.0.1/.github/workflows07070100000003000081A400000000000000000000000160FFEB8C00000677000000000000000000000000000000000000002B00000000koan-3.0.1/.github/workflows/packaging.ymlname: Building Koan packages on: push: branches: - master pull_request: branches: - master jobs: build-centos8-rpms: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Build a CentOS 8 Package shell: 'script -q -e -c "bash {0}"' run: | ./docker/rpms/build-and-install-rpms.sh el8 docker/rpms/CentOS_8/CentOS8.dockerfile build-fedora34-rpms: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Build a Fedora 34 Package shell: 'script -q -e -c "bash {0}"' run: | ./docker/rpms/build-and-install-rpms.sh fc34 docker/rpms/Fedora_34/Fedora34.dockerfile build-opensuse-leap-rpms: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Build a openSUSE Leap 15.3 Package shell: 'script -q -e -c "bash {0}"' run: | ./docker/rpms/build-and-install-rpms.sh opensuse-leap docker/rpms/openSUSE_Leap_15.3/openSUSE_Leap_15.3.dockerfile build-opensuse-tumbleweed-rpms: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Build a openSUSE Tumbleweed Package shell: 'script -q -e -c "bash {0}"' run: | ./docker/rpms/build-and-install-rpms.sh opensuse-tumbleweed docker/rpms/openSUSE_Tumbleweed/openSUSE_TW.dockerfile build-debian-debs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Build a Debian 10 Package shell: 'script -q -e -c "bash {0}"' run: | ./docker/debs/build-and-install-debs.sh deb10 docker/debs/debian/Debian10.dockerfile 07070100000004000081A400000000000000000000000160FFEB8C00000242000000000000000000000000000000000000002400000000koan-3.0.1/.github/workflows/qa.ymlname: Linting Koan on: push: branches: - master pull_request: branches: - master jobs: run_qa: runs-on: ubuntu-20.04 container: registry.opensuse.org/opensuse/tumbleweed:latest steps: - name: Install pre reqs run: zypper -n in git tar - uses: actions/checkout@v2 - name: Install package deps run: zypper -n in --no-recommends python36-wheel python36-pip python36-libvirt-python make - name: Install dependencies run: pip3 install .[lint,test] - name: Run Linters run: make qa 07070100000005000081A400000000000000000000000160FFEB8C00000417000000000000000000000000000000000000002900000000koan-3.0.1/.github/workflows/release.ymlname: Publish Python distributions to TestPyPI on: push: tags: - 'v*' jobs: build-n-publish: name: Build and publish Python distributions to TestPyPI runs-on: ubuntu-latest container: registry.opensuse.org/opensuse/tumbleweed:latest steps: - name: Install pre reqs run: zypper -n in git tar - uses: actions/checkout@v2 - name: Install package deps run: >- zypper -n in --no-recommends python38 python38-base python38-wheel python38-build python38-setuptools python38-pip python38-libvirt-python python38-Sphinx make - name: Install dependencies run: pip3 install .[lint,test,docs] - name: Build a binary wheel and a source tarball run: make release - name: Publish distribution to PyPI if: startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.PYPI_API_TOKEN }} 07070100000006000081A400000000000000000000000160FFEB8C00000526000000000000000000000000000000000000003000000000koan-3.0.1/.github/workflows/release_master.ymlname: Publish Python distributions to TestPyPI on: push: branches: - master jobs: build-n-publish: name: Build and publish Python distributions to TestPyPI runs-on: ubuntu-latest container: registry.opensuse.org/opensuse/tumbleweed:latest steps: - name: Install pre reqs run: zypper -n in git tar - uses: actions/checkout@v2 - name: Install package deps run: >- zypper -n in --no-recommends python38 python38-base python38-wheel python38-build python38-setuptools python38-pip python38-libvirt-python python38-Sphinx make - name: Install dependencies run: pip3 install .[lint,test,docs] - name: Fetch date for version bump run: echo "new_version=$(date +'%Y%m%d%H%M')" >> $GITHUB_ENV - name: Replace version in setup.py run: sed -i '/VERSION = "[0-9].[0-9].[0-9]/s/.$/.'${{ env.new_version }}'"/g' setup.py - name: Build a binary wheel and a source tarball run: make release - name: Publish distribution to Test PyPI uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.TEST_PYPI_API_TOKEN }} repository_url: https://test.pypi.org/legacy/07070100000007000081A400000000000000000000000160FFEB8C000002B3000000000000000000000000000000000000002900000000koan-3.0.1/.github/workflows/testing.ymlname: Testing Koan on: push: branches: - master pull_request: branches: - master jobs: run_tests: runs-on: ubuntu-20.04 container: registry.opensuse.org/opensuse/tumbleweed:latest steps: - name: Install pre reqs run: zypper -n in git tar - uses: actions/checkout@v2 - name: Install package deps run: zypper -n in --no-recommends python36 python36-base python36-wheel python36-pip python36-libvirt-python python36-codecov make - name: Install dependencies run: pip3 install .[lint,test] - name: Run tests run: pytest --cov=./koan - name: Upload report to codecov run: codecov 07070100000008000081A400000000000000000000000160FFEB8C000001F8000000000000000000000000000000000000001600000000koan-3.0.1/.gitignore*.pyc *.swp *.tmp *~ *.class dist rpm-build release build MANIFEST TAGS tags .project .pydevproject .coverage .metadata # Python Folders/Files venv/ cobbler.egg-info # docs docs/*.gz docs/*.html docs/_build # Build output cobbler/webui/master.py config/version # Autogenerated cobbler4j classes, should never be checked in: cobbler4j/src/main/java/org/fedorahosted/cobbler/autogen cobbler4j/user.properties cobbler4j/target # Testrun output test.log # IDE Config Folders .idea/ # Docs docs/_build 07070100000009000081A400000000000000000000000160FFEB8C0000014F000000000000000000000000000000000000001C00000000koan-3.0.1/.readthedocs.yml# .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # Optionally build your docs in additional formats such as PDF and ePub formats: all 0707010000000A000081A400000000000000000000000160FFEB8C000000F4000000000000000000000000000000000000001600000000koan-3.0.1/AUTHORS.inCobbler was originally created by: Michael DeHaan <michael.dehaan@gmail.com> And is maintained by: James Cammarata <jimi@sgnx.net> Jorgen Maas <jorgen.maas@gmail.com> With patches and other contributions from (alphabetically): 0707010000000B000081A400000000000000000000000160FFEB8C00004644000000000000000000000000000000000000001300000000koan-3.0.1/COPYING GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. 0707010000000C000081A400000000000000000000000160FFEB8C00000034000000000000000000000000000000000000001700000000koan-3.0.1/MANIFEST.ininclude COPYING AUTHORS README.md include koan.spec 0707010000000D000081A400000000000000000000000160FFEB8C00000AFB000000000000000000000000000000000000001400000000koan-3.0.1/Makefile TOP_DIR:=$(shell pwd) DESTDIR=/ PYFLAKES = $(shell { command -v pyflakes-3 || command -v pyflakes3 || command -v pyflakes; } 2> /dev/null) PYCODESTYLE := $(shell { command -v pycodestyle-3 || command -v pycodestyle3 || command -v pycodestyle; } 2> /dev/null) all: clean build clean: @echo "cleaning: python bytecode" @rm -f *.pyc @rm -f koan/*.pyc @echo "cleaning: build artifacts" @rm -rf build @rm -rf rpm-build/* @rm -rf deb-build/* @rm -rf release @rm -rf dist @rm -f MANIFEST AUTHORS @rm -f docs/*.1.gz @echo "cleaning: temp files" @rm -f *~ @rm -f *.tmp @rm -f *.log @echo "cleaning: documentation" @cd docs; make clean > /dev/null 2>&1 doc: @echo "creating: documentation" @cd docs; make html > /dev/null 2>&1 qa: ifeq ($(strip $(PYFLAKES)),) @echo "No pyflakes found" else @echo "checking: pyflakes ${PYFLAKES}" @${PYFLAKES} *.py bin/koan bin/cobbler-register koan/*.py endif ifeq ($(strip $(PYCODESTYLE)),) @echo "No pycodestyle found" else @echo "checking: pycodestyle" @${PYCODESTYLE} -r --ignore E303,E501,W504,E722 \ *.py bin/koan bin/cobbler-register koan/*.py endif authors: @echo "creating: AUTHORS" @cp AUTHORS.in AUTHORS @git log --format='%aN <%aE>' | grep -v 'root' | sort -u >> AUTHORS sdist: authors @echo "creating: sdist" @python3 setup.py sdist > /dev/null release: clean qa authors sdist doc @echo "creating: release artifacts" @mkdir release @cp dist/*.gz release/ @cp koan.spec release/ nosetests: PYTHONPATH=./koan/ nosetests -v -w tests/cli/ 2>&1 | tee test.log build: python3 setup.py build -f # Debian/Ubuntu requires an additional parameter in setup.py install: build if [ -e /etc/debian_version ]; then \ python3 setup.py install --root $(DESTDIR) -f --install-layout=deb; \ else \ python3 setup.py install --root $(DESTDIR) -f; \ fi savestate: python3 setup.py -v savestate --root $(DESTDIR); \ rpms: release mkdir -p rpm-build cp dist/*.gz rpm-build/ rpmbuild --define "_topdir %(pwd)/rpm-build" \ --define "_builddir %{_topdir}" \ --define "_rpmdir %{_topdir}" \ --define "_srcrpmdir %{_topdir}" \ --define "_specdir %{_topdir}" \ --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ --define "_sourcedir %{_topdir}" \ -ba koan.spec # Only build a binary package debs: release ## Runs the target release and then creates via debbuild the debs in a directory called deb-build. mkdir -p deb-build mkdir -p deb-build/{BUILD,BUILDROOT,DEBS,SDEBS,SOURCES} cp dist/*.gz deb-build/ debbuild --define "_topdir %(pwd)/deb-build" \ --define "_builddir %{_topdir}" \ --define "_specdir %{_topdir}" \ --define "_sourcedir %{_topdir}" \ -vv -bb koan.spec .PHONY: tags tags: find . \( -name build -o -name .git \) -prune -o -type f -name '*.py' -print | xargs etags -o TAGS -- 0707010000000E000081A400000000000000000000000160FFEB8C000004AD000000000000000000000000000000000000001500000000koan-3.0.1/README.md[![Testing Koan](https://github.com/cobbler/koan/actions/workflows/testing.yml/badge.svg)](https://github.com/cobbler/koan/actions/workflows/testing.yml) [![Building Koan packages](https://github.com/cobbler/koan/actions/workflows/packaging.yml/badge.svg)](https://github.com/cobbler/koan/actions/workflows/packaging.yml) [![codecov](https://codecov.io/gh/cobbler/koan/branch/master/graph/badge.svg?token=wSeiJxpVNh)](https://codecov.io/gh/cobbler/koan) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/41f1b5564c9d47d2a7ad6458b2011b9b)](https://www.codacy.com/gh/cobbler/koan/dashboard?utm_source=github.com&utm_medium=referral&utm_content=cobbler/koan&utm_campaign=Badge_Grade) [![Gitter](https://badges.gitter.im/cobbler/koan.svg)](https://gitter.im/cobbler/koan?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Documentation Status](https://readthedocs.org/projects/koan/badge/?version=latest)](https://koan.readthedocs.io/en/latest/?badge=latest) # Koan Koan stands for kickstart-over-a-network and allows for both network installation of new virtualized guests and reinstallation of an existing system. For use with a boot-server configured with Cobbler. 0707010000000F000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000000F00000000koan-3.0.1/bin07070100000010000081ED00000000000000000000000160FFEB8C00000232000000000000000000000000000000000000002000000000koan-3.0.1/bin/cobbler-register#!/usr/bin/env python """ cobbler-register wrapper script. See 'man cobbler-register' for details. Copyright 2009 Red Hat, Inc and Others. Michael DeHaan <michael.dehaan AT gmail> This software may be freely redistributed under the terms of the GNU general public license. 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. """ import sys import koan.register as register sys.exit(register.main() or 0) 07070100000011000081ED00000000000000000000000160FFEB8C00000210000000000000000000000000000000000000001400000000koan-3.0.1/bin/koan#!/usr/bin/env python """ Koan wrapper script. See 'man koan' for details. Copyright 2006-2009 Red Hat, Inc and Others. Michael DeHaan <michael.dehaan AT gmail> This software may be freely redistributed under the terms of the GNU general public license. 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. """ import sys import koan.app as app sys.exit(app.main() or 0) 07070100000012000081ED00000000000000000000000160FFEB8C00001FB6000000000000000000000000000000000000001B00000000koan-3.0.1/bin/ovz-install#!/usr/bin/env bash ## OpenVZ container-type virtualization installation functions. ## ## Copyright 2012 Sergey Podushkin <psv AT tncc.ru> ## ## 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 ## PROFILE_NAME=$1 KICKSTART_URL=$2 ROOTDIR=$3 if [ -z "$PROFILE_NAME" -o -z "$ROOTDIR" -o -z "$KICKSTART_URL" ] ; then echo "Some arguments missing!" echo "Usage: $0 system_name kickstart_url private_dir" echo "Exiting..." exit 1 fi PATH=/bin:/sbin:/usr/bin:/usr/sbin KICKSTART="/tmp/$PROFILE_NAME-kickstart.cfg" # get the kickstart curl $KICKSTART_URL -s -o $KICKSTART # get the root password hash from kickstart ROOTPW=`cat $KICKSTART| awk '/^rootpw/{ print $NF }'` # what shell will be used for post-install script? get it from kickstart POST_INSTALL_SHELL=`cat $KICKSTART | grep '^%post.*--interpreter' | sed -n 's/^.*--interpreter \([^ ][^ ]*\).*/\1/;p'` # if not defined in kickstart, then use /bin/sh [ -z $POST_INSTALL_SHELL ] && POST_INSTALL_SHELL="/bin/sh" # where to store post-install script POST_INSTALL_SCRIPT="/tmp/$PROFILE_NAME-post-install" # add postinstall script from kickstart cat $KICKSTART | sed -n '0,/\%post/d;p' >$POST_INSTALL_SCRIPT # get list of services that should be enabled after installation (anaconda-like behaviour) SERVICES_ENABLED=`cat $KICKSTART| grep '^services.*--enabled' | sed 's/^services.*--enabled//; s/,/ /g'` # get list of services that should be disabled after installation (anaconda-like behaviour) SERVICES_DISABLED=`cat $KICKSTART| grep '^services.*--disabled' | sed 's/^services.*--disabled//; s/,/ /g'` # some our anaconda-like actions before postinstall script execution SERVICES_SCRIPT="/tmp/$PROFILE_NAME-services.sh" cat /dev/null >$SERVICES_SCRIPT # disable+enable service as directed in kickstart options # first disable services for serv in $SERVICES_DISABLED ; do echo chkconfig --level 345 $serv off >>$SERVICES_SCRIPT done # then enable services for serv in $SERVICES_ENABLED ; do echo chkconfig --level 345 $serv on >>$SERVICES_SCRIPT done # temporary yum config YUM_CONFIG="/tmp/$PROFILE_NAME-yum.cfg" echo -e "[main]\ncachedir=/var/cache/yum/\$basearch/\$releasever\nkeepcache=0\ndebuglevel=2\nlogfile=/var/log/yum.log\nexactarch=1\nobsoletes=1\ngpgcheck=0\nplugins=1\ndistroverpkg=centos-release\nreposdir=/dev/null\n" >$YUM_CONFIG echo -e "groupremove_leaf_only=1\ngroup_package_types=mandatory\ntsflags=nodocs\n" >>$YUM_CONFIG # --ignoremissing processing cat $KICKSTART| grep '\-\-ignoremissing'>/dev/null if [ $? -eq 0 ] ; then echo -e "skip_broken=1\n" >>$YUM_CONFIG ; fi # just new line echo >>$YUM_CONFIG # base package set we get from kickstart's url option (this option used only for http/ftp install, that is in use by cobbler, if kickstart use other method we'll FAIL!!!) BASE_REPO_URL=`cat $KICKSTART| grep ^url | sed 's/^url.*--url=//'` # put in to our config echo -e "[base-os]\nname=base-os\nbaseurl=$BASE_REPO_URL\nenabled=1\npriority=1\ngpgcheck=0\n\n" >>$YUM_CONFIG # get additional repos from kickstart and put it to config too cat $KICKSTART | grep ^repo | \ sed 's/^repo\ //; s/--//g' | \ while read repo_name repo_url ; do repo_tag=`echo $repo_name | sed 's/name=//'` echo -e "[$repo_tag]\n$repo_name\n$repo_url\nenabled=1\npriority=99\ngpgcheck=0\n" >>$YUM_CONFIG done # packages we don't need to install (but included in installed groups) EXCLUDED_PKGS="selinux-policy-targeted kernel* *firmware* b43*" # packages we want to be installed, besides of listed in kickstart PKGS_LIST="vim-minimal ssh-clients openssh-server logrotate" # temporary yum script YUM_SCRIPT="/tmp/$PROFILE_NAME-yum.yum" cp /dev/null $YUM_SCRIPT (echo config assumeyes True echo config gpgcheck False echo install $PKGS_LIST ) >>$YUM_SCRIPT if [ -n "$EXCLUDED_PKGS" ] ; then echo config exclude \"$EXCLUDED_PKGS\" >>$YUM_SCRIPT ; fi cat $KICKSTART| awk '/^\%packages/,/^\%post/{ print $0 }'|egrep -v '^#|^$|^%' | \ while read line ; do # if package name can start with '-' sign, that means we have to exclude it ACTION="install" IS_GROUP="" echo $line|grep '^-'>/dev/null if [ $? -eq 0 ] ; then line=`echo $line|sed 's/^-//'` ACTION="remove" fi echo $line|grep '^@' >/dev/null # if name starts with @ - it's a group if [ $? -eq 0 ] ; then line=`echo $line|sed 's/^@//'` IS_GROUP="group" fi line=`echo $line | sed 's/^\s*//'` echo ${IS_GROUP}${ACTION} \"$line\" >>$YUM_SCRIPT done cat $KICKSTART| grep '\-\-nobase'>/dev/null if [ $? -eq 0 ] ; then echo groupremove base >>$YUM_SCRIPT ; fi echo run >>$YUM_SCRIPT # install all packages in one pass by using yum shell #### THIS IS LONG-RUNNING TASK! ###### echo Start installing packages yum shell --quiet --config=$YUM_CONFIG --installroot=$ROOTDIR $YUM_SCRIPT ## >/dev/null 2>&1 # some optimization yum remove kernel kernel-firmware dracut dracut-kernel dracut-network fcoe-utils libdrm lldpad plymouth -y --quiet --config=$YUM_CONFIG --installroot=$ROOTDIR echo Packages installed # remove all *.repo files, cobbler will install it's own repo-file with needed repos rm -f $ROOTDIR/etc/yum.repos.d/*.repo # move services setup script in container root, to be reachable inside of chroot mv $SERVICES_SCRIPT $ROOTDIR/$SERVICES_SCRIPT # turn off and on services in chroot echo Disabling and enabling services as needed chroot $ROOTDIR /bin/bash $SERVICES_SCRIPT # move postinstall script in container root, to be reachable inside of chroot mv $POST_INSTALL_SCRIPT $ROOTDIR/$POST_INSTALL_SCRIPT # run the postinstall actions in chroot echo Perform post-installation actions (chroot $ROOTDIR $POST_INSTALL_SHELL $POST_INSTALL_SCRIPT )>/dev/null 2>&1 # tune the installations to be suitable for OpenVZ as environment echo Make the tree container-ready cd $ROOTDIR # remove unneeded upstart scripts rm -f $ROOTDIR/etc/init/control-alt-delete.conf rm -f $ROOTDIR/etc/init/plymouth-shutdown.conf rm -f $ROOTDIR/etc/init/prefdm.conf rm -f $ROOTDIR/etc/init/quit-plymouth.conf rm -f $ROOTDIR/etc/init/rcS-sulogin.conf rm -f $ROOTDIR/etc/init/serial.conf rm -f $ROOTDIR/etc/init/start-ttys.conf rm -f $ROOTDIR/etc/init/tty.conf sed -i -e 's/^console/#console/' $ROOTDIR/etc/init/rc.conf sed -i -e 's/^console/#console/' $ROOTDIR/etc/init/rcS.conf # tune sshd sed -i -e 's/GSSAPIAuthentication\ yes/GSSAPIAuthentication\ no/g' $ROOTDIR/etc/ssh/sshd_config # turn off SELinux mkdir -p $ROOTDIR/etc/selinux echo SELINUX=disabled>$ROOTDIR/etc/selinux/config # we use ! as the delimiter for sed, because $ROOTPW hash is full of weird signs ;) sed -i -e "s!root:.:!root:$ROOTPW:!" $ROOTDIR/etc/shadow # who needs it?! #echo "PS1='[\u@\h \W]\$'" >> /etc/profile # link mtab from outer space [ -f $ROOTDIR/etc/mtab ] && rm $ROOTDIR/etc/mtab ln -s /proc/mounts etc/mtab # some point to fstab echo "none /dev/pts devpts rw,gid=5,mode=620 0 0">$ROOTDIR/etc/fstab # here is plain file dev/null, so we remove it rm -f $ROOTDIR/dev/null # create neccessary device files for dir in $ROOTDIR/dev $ROOTDIR/etc/udev/devices ; do /sbin/MAKEDEV -d $dir -x {p,t}ty{a,p}{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f} console core full kmem kmsg mem null port ptmx random urandom zero ram0 ln -s /proc/self/fd $dir/fd ln -s /proc/self/fd/2 $dir/stderr ln -s /proc/self/fd/0 $dir/stdin ln -s /proc/self/fd/1 $dir/stdout done # ajust permissions chmod 1777 $ROOTDIR/tmp chmod 1777 $ROOTDIR/var/tmp echo All done exit 0 07070100000013000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001200000000koan-3.0.1/docker07070100000014000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001700000000koan-3.0.1/docker/debs07070100000015000081ED00000000000000000000000160FFEB8C0000060B000000000000000000000000000000000000003100000000koan-3.0.1/docker/debs/build-and-install-debs.sh#!/bin/bash # Utility script to build DEBs in a Docker container and then install them set -euo pipefail if [ "$1" == "--with-tests" ] then RUN_TESTS=true shift else RUN_TESTS=false fi TAG=$1 DOCKERFILE=$2 IMAGE=koan:$TAG # Build container echo "==> Build container ..." docker build -t "$IMAGE" -f "$DOCKERFILE" . # Build DEBs echo "==> Build packages ..." mkdir -p deb-build tmp docker run --rm -ti -v "$PWD/deb-build:/usr/src/koan/deb-build" -v "$PWD/tmp:/var/tmp" "$IMAGE" # Launch container and install Koan echo "==> Start container ..." docker run -t -d --name koan -v "$PWD/deb-build:/usr/src/koan/deb-build" "$IMAGE" /bin/bash echo "==> Install fresh packages ..." docker exec -it koan bash -c 'dpkg -i deb-build/DEBS/all/python3-koan*.deb' docker exec -it koan bash -c 'dpkg -i deb-build/DEBS/all/koan*.deb' # Does not work because of wrong exit code. Koan has not help or version switch which means we need to skip this for now #echo "==> Wait 5 sec. and show Koan version ..." #docker exec -it koan bash -c 'koan' if $RUN_TESTS then # Almost all of these requirement are already satisfied in the Dockerfiles! echo "==> Running tests ..." docker exec -it koan bash -c 'pip3 install coverage distro setuptools sphinx netaddr distro' docker exec -it koan bash -c 'pip3 install pyflakes pycodestyle pytest pytest-cov codecov' docker exec -it koan bash -c 'pytest-3' fi # Clean up echo "==> Stop Koan container ..." docker stop koan echo "==> Delete Koan container ..." docker rm koan rm -rf ./tmp07070100000016000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001E00000000koan-3.0.1/docker/debs/debian07070100000017000081A400000000000000000000000160FFEB8C0000067B000000000000000000000000000000000000003200000000koan-3.0.1/docker/debs/debian/Debian10.dockerfile# vim: ft=dockerfile FROM debian:10 ENV DEBIAN_FRONTEND noninteractive # TERM=screen is fairly neutral and works with xterm for example, for others # you might need to pass -e TERM=<terminal>, like rxvt-unicode. ENV TERM screen ENV OSCODENAME buster # Add repo for debbuild and install all packages required # hadolint ignore=DL3008,DL3015,DL4006 RUN apt-get update -qq && \ apt-get install -qqy gnupg curl && \ /bin/sh -c "echo 'deb http://download.opensuse.org/repositories/Debian:/debbuild/Debian_10/ /' > /etc/apt/sources.list.d/debbuild.list" && \ curl -sL http://download.opensuse.org/repositories/Debian:/debbuild/Debian_10/Release.key | apt-key add - && \ apt-get update -qq && \ apt-get install -qqy \ debbuild \ debbuild-macros \ wget \ pycodestyle \ python3-pyflakes \ python3-coverage \ python3-wheel \ python3-distro \ python3-libvirt \ python3-netifaces \ python3-distutils \ python3-pip \ python3-pycodestyle \ python3-pytest \ python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-tz \ virtinst \ liblocale-gettext-perl \ lsb-release \ xz-utils \ bzip2 \ dpkg-dev \ rsync \ fakeroot \ patch \ pax \ git \ hardlink && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Make /bin/sh point to bash, not dash SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN echo "dash dash/sh boolean false" | debconf-set-selections && \ dpkg-reconfigure dash COPY . /usr/src/koan WORKDIR /usr/src/koan VOLUME /usr/src/koan/deb-build CMD ["/bin/bash", "-c", "make debs"] 07070100000018000081ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000002700000000koan-3.0.1/docker/debs/install-debs.sh07070100000019000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001700000000koan-3.0.1/docker/rpms0707010000001A000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000002000000000koan-3.0.1/docker/rpms/CentOS_80707010000001B000081A400000000000000000000000160FFEB8C00000416000000000000000000000000000000000000003300000000koan-3.0.1/docker/rpms/CentOS_8/CentOS8.dockerfile# vim: ft=dockerfile FROM centos:8 RUN dnf makecache && \ dnf install -y epel-release dnf-utils && \ dnf config-manager --set-enabled powertools && \ dnf makecache # overlay2 bug with yum/dnf # # OverlayFS only implements a subset of POSIX standards. This can cause RPM db corruption. # See bottom of https://docs.docker.com/storage/storagedriver/overlayfs-driver/ # Since there is no dnf-plugin-ovl for CentOS 8 yet, we need to touch /var/lib/rpm/* before # 'dnf install' to avoid the issue. # Dev dependencies RUN touch /var/lib/rpm/* && \ dnf install -y \ git \ make \ rpm-build \ epel-rpm-macros \ virt-install \ python3-devel \ python3-setuptools \ python3-sphinx \ python3-distro \ python3-netifaces \ python3-libvirt COPY . /usr/src/koan WORKDIR /usr/src/koan VOLUME /usr/src/koan/rpm-build CMD ["/bin/bash", "-c", "make rpms"] 0707010000001C000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000002100000000koan-3.0.1/docker/rpms/Fedora_340707010000001D000081A400000000000000000000000160FFEB8C00000227000000000000000000000000000000000000003500000000koan-3.0.1/docker/rpms/Fedora_34/Fedora34.dockerfile# vim: ft=dockerfile FROM fedora:34 RUN dnf makecache # Dev dependencies RUN dnf install -y \ git \ make \ rpm-build \ virt-install \ python3-devel \ python3-setuptools \ python3-sphinx \ python3-sphinx_rtd_theme \ python3-distro \ python3-netifaces \ python3-libvirt COPY . /usr/src/koan WORKDIR /usr/src/koan VOLUME /usr/src/koan/rpm-build CMD ["/bin/bash", "-c", "make rpms"] 0707010000001E000081ED00000000000000000000000160FFEB8C00000580000000000000000000000000000000000000003100000000koan-3.0.1/docker/rpms/build-and-install-rpms.sh#!/bin/bash # Utility script to build RPMs in a Docker container and then install them set -eo pipefail if [ "$1" == "--with-tests" ] then RUN_TESTS=true shift else RUN_TESTS=false fi TAG=$1 DOCKERFILE=$2 IMAGE=koan:$TAG # Build container echo "==> Build container ..." docker build -t "$IMAGE" -f "$DOCKERFILE" . # Build RPMs echo "==> Build RPMs ..." mkdir -p rpm-build docker run -ti -v "$PWD/rpm-build:/usr/src/koan/rpm-build" "$IMAGE" # Launch container and install koan echo "==> Start container ..." docker run -t -d --name koan -v "$PWD/rpm-build:/usr/src/koan/rpm-build" "$IMAGE" /bin/bash echo "==> Install fresh RPMs ..." docker exec -it koan bash -c 'rpm -Uvh rpm-build/python3-koan-*.noarch.rpm' docker exec -it koan bash -c 'rpm -Uvh rpm-build/koan-*.noarch.rpm' # Does not work because of wrong exit code. Koan has not help or version switch which means we need to skip this for now #echo "==> Show Koan version ..." #docker exec -it koan bash -c 'koan version' if $RUN_TESTS then echo "==> Running tests ..." docker exec -it koan bash -c 'pip3 install coverage distro setuptools sphinx requests netifaces' docker exec -it koan bash -c 'pip3 install pyflakes pycodestyle pytest pytest-cov codecov' docker exec -it koan bash -c 'pytest' fi # Clean up echo "==> Stop Koan container ..." docker stop koan echo "==> Delete Koan container ..." docker rm koan 0707010000001F000081ED00000000000000000000000160FFEB8C0000046E000000000000000000000000000000000000002700000000koan-3.0.1/docker/rpms/install-rpms.sh#!/bin/bash # Utility script to run Docker container without building the RPMs, # just install them. So make sure they are in rpm-build dir! if [ "$1" == "--with-tests" ] then RUN_TESTS=true shift else RUN_TESTS=false fi TAG=$1 IMAGE=koan:$TAG # Launch container and install Koan echo "==> Start container ..." docker run -d --name koan -v "$PWD/rpm-build:/usr/src/cobbler/rpm-build" "$IMAGE" /bin/bash echo "==> Install fresh RPMs ..." docker exec -it koan bash -c 'rpm -Uvh rpm-build/cobbler-*.noarch.rpm' # Does not work because of wrong exit code. Koan has not help or version switch which means we need to skip this for now #echo "==> Show Koan version ..." #docker exec -it koan bash -c 'koan version' if $RUN_TESTS then echo "==> Running tests ..." docker exec -it koan bash -c 'pip3 install coverage distro setuptools sphinx requests netifaces' docker exec -it koan bash -c 'pip3 install pyflakes pycodestyle pytest pytest-cov codecov' docker exec -it koan bash -c 'pytest' fi # Clean up echo "==> Stop Koan container ..." docker stop koan echo "==> Delete Koan container ..." docker rm koan 07070100000020000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000002A00000000koan-3.0.1/docker/rpms/openSUSE_Leap_15.307070100000021000081A400000000000000000000000160FFEB8C00000373000000000000000000000000000000000000004800000000koan-3.0.1/docker/rpms/openSUSE_Leap_15.3/openSUSE_Leap_15.3.dockerfile# vim: ft=dockerfile FROM registry.opensuse.org/opensuse/leap:15.3 # ENV Variables we are using. ENV container docker ENV DISTRO SUSE # Update Leap to most current packages RUN zypper update -y # Runtime & dev dependencies RUN zypper install -y \ git \ make \ rpm-build \ virt-install \ python3 \ python3-base \ python3-devel \ python3-wheel \ #python3-build \ <-- Not available on Leap 15.3 python3-setuptools \ python3-pip \ python3-libvirt-python \ python3-distro \ python3-netifaces \ python3-Sphinx # Build RPMs COPY . /usr/src/koan WORKDIR /usr/src/koan VOLUME /usr/src/koan/rpm-build CMD ["/bin/bash", "-c", "make rpms"]07070100000022000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000002B00000000koan-3.0.1/docker/rpms/openSUSE_Tumbleweed07070100000023000081A400000000000000000000000160FFEB8C00000369000000000000000000000000000000000000004200000000koan-3.0.1/docker/rpms/openSUSE_Tumbleweed/openSUSE_TW.dockerfile# vim: ft=dockerfile FROM registry.opensuse.org/opensuse/tumbleweed:latest # ENV Variables we are using. ENV container docker ENV DISTRO SUSE # Update Leap to most current packages RUN zypper dup -y # Runtime & dev dependencies RUN zypper install -y \ git \ make \ rpm-build \ virt-install \ python38 \ python38-base \ python38-devel \ python38-wheel \ python38-build \ python38-setuptools \ python38-pip \ python38-libvirt-python \ python38-distro \ python38-netifaces \ python38-Sphinx # Build RPMs COPY . /usr/src/koan WORKDIR /usr/src/koan VOLUME /usr/src/koan/rpm-build CMD ["/bin/bash", "-c", "make rpms"] 07070100000024000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001000000000koan-3.0.1/docs07070100000025000081A400000000000000000000000160FFEB8C00000252000000000000000000000000000000000000001900000000koan-3.0.1/docs/Makefile# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = Koan SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 07070100000026000081A400000000000000000000000160FFEB8C00000827000000000000000000000000000000000000002500000000koan-3.0.1/docs/cobbler-register.rstCobbler-Register **************** cobbler-register - create a Cobbler system record Synopsis ######## .. code-block:: shell cobbler-register [--server=<cobbler.example.org>] --profile=<cobbler-profile-name> [--fqdn=<hostname>] Description ########### Running cobbler-register on a system will create a Cobbler system record for that system on a remote Cobbler server. No changes will be made on the system itself. Details ####### When installing new machines into a Cobbler managed datacenter/lab, it helps to not have to manually enter in the network information for those systems. Using ``cobbler-register`` either from a kickstart or a live environment (or even SSH) can help seed the Cobbler database. Network information is discovered automatically for all physical interfaces. ``cobbler-register`` will attempt to discover the hostname, though if `localhost.localdomain` is found, it will have to use some other data for the Cobbler system record. This is probably not what you want, so specify ``--fqdn`` in this instance to override that registration value. For this to work, the ``register_new_installs`` setting must be enabled on the remote Cobbler server. When the remote system record is created, for safety reasons, it will be set in Cobbler to be "netboot disabled". Use ``cobbler system edit --name=foo --netboot-enabled=1`` to set the machine to reinstall, where "foo" is the name of the new system record. Environment variables ##################### cobbler-register respects the `COBBLER_SERVER` variable to specify the Cobbler server to use. This is a convenient way to avoid using the ``--server`` option. This variable is set automatically on systems installed via Cobbler, assuming standard kickstart templates are used. If you need to change this on an installed system, edit ``/etc/profile.d/cobbler.{csh,sh}``. Additional ########## Reading the Koan manpage, www.cobbler.github.io or this Readthedocs-Project is highly recommended. Author ###### Michael DeHaan <michael.dehaan AT gmail> Revised by: Enno Gotthold <matrixfueller@gmail.com> 07070100000027000081A400000000000000000000000160FFEB8C000022DB000000000000000000000000000000000000001800000000koan-3.0.1/docs/conf.py# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) # -- Project configuration ----------------------------------------------------- project = 'Koan' copyright = '2014, Jörgen Maas' author = 'Jörgen Maas' # The short X.Y version version = '3.0' # The full version, including alpha/beta/rc tags release = '3.0.1' # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { 'canonical_url': '', # 'analytics_id': '', # Provided by Google in your dashboard 'logo_only': False, 'display_version': True, 'prev_next_buttons_location': 'bottom', # Toc options 'collapse_navigation': True, 'sticky_navigation': True, 'navigation_depth': 4, } # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # "<project> v<release> documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a <link> tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Koandoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). 'papersize': 'a4paper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ (master_doc, 'Koan.tex', u'Koan Documentation', 'Jörgen Maas', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('koan', 'koan', u'Koan Documentation', [u'Jörgen Maas'], 1), ('cobbler-register', 'cobbler-register', u'Cobbler-Register Documentation', [u'Jörgen Maas'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'Koan', u'Koan Documentation', u'Jörgen Maas', 'Koan', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # -- Options for Epub output ------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # -- Extension configuration ------------------------------------------------- # -- Options for intersphinx extension --------------------------------------- # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} # -- Options for todo extension ---------------------------------------------- # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True 07070100000028000081A400000000000000000000000160FFEB8C00000252000000000000000000000000000000000000001A00000000koan-3.0.1/docs/index.rstWelcome to Koans's documentation! *********************************** Koan - kickstart over a network, client side helper for cobbler Contents: .. toctree:: :maxdepth: 2 Koan <koan> cobbler-register <cobbler-register> Koan on OpenVZ <koan-on-openvz> Koan with ISO's <koan-with-isos> Installing virtual guests <installing-virtual-guests> Reinstallation <reinstallation> Virtual Networking Setup <virtual-networking-setup> VMWare <vmware> Release Notes <release-notes> Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` 07070100000029000081A400000000000000000000000160FFEB8C00000D77000000000000000000000000000000000000002E00000000koan-3.0.1/docs/installing-virtual-guests.rst************************* Installing virtual guests ************************* The main feature of Koan is contact the Cobbler server, learn about a configuration from Cobbler, and make that virtualized installation happen. One other feature is [KoanDoesReinstall reinstallation of existing systems] but perhaps the more important one is how to do virtualized installs. Koan is its own separate tool, a separate package from Cobbler, that is designed for use with a remote Cobbler server. (The same folks that work on Cobbler work on Koan and it's available from the same repositories) .. code-block:: shell yum install koan It is a very small tool and generally does not always need to be updated when Cobbler is updated, but keeping Koan updated ensures you have all the latest features available. In general, the major release numbers of Cobbler and Koan should match, but it's not so important if the minor release numbers don't match. Example of installing a VM using a profile virtually #################################################### .. code-block:: shell koan --server=cobbler.example.org --virt --profile=foo Example of installing a VM using a system record ################################################ .. code-block:: shell koan --server=cobbler.example.org --virt --system=foo Overrides ######### Koan is designed to install things as set up in Cobbler to ensure installs are consistent and repeatable. Often though, users of Koan may not be Cobbler server administrators or may want to install a VM on a test system -- so they'll want to override some things as stored in Cobbler. Koan allows an extensive system of overrides to tweak what Cobbler tells us about how a particular Cobbler profile should be installed. +-------------------+---------------------------------------+---------------------------+ | Flag | Explanation | Example | +===================+=======================================+===========================+ | ``--virt-name`` | name of virtual machine to create | testmachine | +-------------------+---------------------------------------+---------------------------+ | ``--virt-type`` | forces usage of qemu/xen/vmware | qemu | +-------------------+---------------------------------------+---------------------------+ | ``--virt-bridge`` | name of bridge device | virbr0 | +-------------------+---------------------------------------+---------------------------+ | ``--virt-path`` | overwrite this disk partition | `/dev/sda4` | +-------------------+---------------------------------------+---------------------------+ | ``--virt-path`` | use this directory | `/opt/myimages` | +-------------------+---------------------------------------+---------------------------+ | ``--virt-path`` | use this existing LVM volume | `VolGroup00` | +-------------------+---------------------------------------+---------------------------+ | ``--nogfx`` | do not use VNC graphics (Xen only) | (does not take options) | +-------------------+---------------------------------------+---------------------------+ Nearly all of these variables can also be defined and centrally managed by the Cobbler server and are also described in the Cobbler manpage in depth. 0707010000002A000081A400000000000000000000000160FFEB8C000010F2000000000000000000000000000000000000002300000000koan-3.0.1/docs/koan-on-openvz.rst**************************************** Support for OpenVZ containers in Cobbler **************************************** THIS FUNCTIONS CONSIDERED AS ALPHA STAGE FOR TESTING AND LIMITED USAGE! USAGE IN PRODUCTION CAN BE DANGEROUS! YOU WARNED! Cobbler is amazing tool for deploying barebones and virtual machines and I think it is suitable for deploying OpenVZ containers too. Current support for OpenVZ is rather basic, but I think this functionality can reach level we have now for KVM. How to use it? ############## Because OpenVZ container is in nature chrooted environment we use Cobbler + Koan to create this on OpenVZ-enabled node. For Cobbler and Koan in case of OpenVZ all operations is similar - we should define distros, automated installation files, profiles, systems and so on with some additions. Now we do all operations only for RHEL/CentOS 6. It may be suitable for recent Fedoras, but we do nothing for other distributions. How it works? ############# All options keeps on cobbler side as for other VMs. Besides of common options you can use openvz-specific ones by defining them as ``vz_`` prefixed, low-cased variables from this list: KMEMSIZE, LOCKEDPAGES, PRIVVMPAGES, SHMPAGES, NUMPROC, VMGUARPAGES, OOMGUARPAGES, NUMTCPSOCK, NUMFLOCK, NUMPTY, NUMSIGINFO, TCPSNDBUF, TCPRCVBUF, OTHERSOCKBUF, DGRAMRCVBUF, NUMOTHERSOCK, DCACHESIZE, NUMFILE, AVNUMPROC, NUMIPTENT, DISKINODES, QUOTATIME, VE_ROOT, VE_PRIVATE, SWAPPAGES, ONBOOT (See ctid.conf(5) for meaning of this parameters). Because cobbler does not have a place to keep CTID you MUST use it in ks_meta (as you can see in example below)! We use it on cobbler-side to be able allocate them from one place. We turn off PXE-menu creation for OpenVZ containers to not pollute this menu. For example: .. code-block:: shell # cobbler profile add --name=vz01 --distro=CentOS6-x86_64 --autoinst=/your/autoinst.cfg \ --ks_meta="lang=ru_RU.UTF-8 keyb=ru vz_ctid=101 vz_swappages=0:2G vz_numproc=120:120" \ --repos="centos6-x86_64-os centos-x86_64-updates" \ --virt-type=openvz \ --virt-ram=1024 \ --virt-cpus=1 # cobbler system add --name=vz01 \ --profile=vz01 \ --virt-type=openvz \ --virt-ram=1024 \ --virt-cpus=1 # cobbler system edit --name=vz01 \ --hostname=vz01.example.com \ --interface=eth0 \ --mac=YOUR_MAC_HERE \ --static=1 \ --ip-address=YOUR_IP \ --netmask=MASK \ --gateway=GATEWAY_IP \ --name-servers=NAME_SERVERS_IPs On the Koan side: .. code-block:: none # koan --server=COBBLER_IP --virt --system=vz01 This will start installation process. ovz-install script will install all packages and groups listed in $packages section. As root for installation ovz-install will use /vz/private/$VEID (/vz/private/101 for example above), that can be overriden with vz_ve_private variable in ks_meta (eg. vz_ve_private=/some/path or vz_ve_private=/other/path/$VEID or vz_ve_private=/some/path/101 - $VEID will be replaced with CTID). After installation ovz-install will process "services" option from autoinst like it do anaconda and run post-installation script, defined in autoinst (only in chroot), so you can tune the container for your needs. At the end of process ovz-install process installed tree to be truly OpenVZ container - creates dev files, change init scripts etc. Created container started after that, so you should be able to log in to it with root and password you defined for root in autoinst file. Options for creating OpenVZ containers ###################################### You should set virt-type to "openvz" in profile or system to create OpenVZ container. .. code-block:: none --virt-file-size not used for now. We think we can use it for logical volume creation, or quoting filesystem usage, or for creating containers in ploop-file. --virt-ram as for other VMs --virt-cpus as for other VMs --virt-path not used now. Container will be created in /vz/private/$VEID, where $VEID will be replaced by openvz with CTID (container ID). Can be redefined by vz_ve_private variable you can place in ks_meta. --virt_bridge not used now. 0707010000002B000081A400000000000000000000000160FFEB8C00000645000000000000000000000000000000000000002300000000koan-3.0.1/docs/koan-with-isos.rst*************** Koan with ISO's *************** While most of Cobbler installing is about enabling scripted network installation, can also define image objects which track ISOs that Koan can find and see. Currently this only works when using Koan with QEMU/KVM for installation (sorry, no Xen or VMware support yet). This can be used, for instance, to install Windows via Koan. For this to work best, the ISO must be available by the same NFS path on all hosts. It need not be mounted and Cobbler nor Koan will copy it. .. code-block:: shell cobbler image add --name=image_name --file=nfs://hostname.example.org:/path/example/acme-os-installer-image.iso [--virt-ram=512] [--virt-file-size=10] [...etc...] And on the Koan side, just run: .. code-block:: shell koan --list=images --server=cobbler.example.org koan --virt --image=image_name --server=cobbler.example.org Koan will then mount the NFS location and begin a fully virtualized installation using the virtual metadata and info stored in Cobbler. You may remember that Cobbler has objects like "distros" and "profiles". Images are another type of object, but act similarly. System objects in Cobbler may also attach to an image instead of a profile, though not all attributes of the system apply to an image -- for instance, we may care about the number of interfaces, but the networking configuration automagic that normally happens as part of a kickstart can't happen for an image based install. Avoid image based installs if at all possible -- use kickstart where you can, and images for foreign content where you can not. 0707010000002C000081A400000000000000000000000160FFEB8C000016E4000000000000000000000000000000000000001900000000koan-3.0.1/docs/koan.rstKoan **** Koan - kickstart over a network, client side helper for Cobbler Synopsis ######## .. code-block:: shell koan --server=hostname [--list=type] [--virt|--replace-self|--display] [--profile=name] [--system=name] [--image=name] [--add-reinstall-entry] [--virt-name=name] [--virt-path=path] [--virt-type=type] [--nogfx] [--static-interface=name] [--kexec] Description ########### Koan stands for "kickstart-over-a-network" and is a client-side helper program for use with Cobbler. Koan allows for both network provisioning of new virtualized guests (Xen, QEMU/KVM, VMware) and re-installation of an existing system. When invoked, Koan requests install information from a remote Cobbler boot server, it then kicks off installations based on what is retrieved from Cobbler and fed in on the Koan command line. The examples below show the various use cases. Listing remote Cobbler objects ############################## To browse remote objects on a Cobbler server and see what you can install using Koan, run one of the following commands: .. code-block:: shell koan --server=cobbler.example.org --list=profiles koan --server=cobbler.example.org --list=systems koan --server=cobbler.example.org --list=images Learn more about Cobbler objects ################################ To learn more about what you are about to install, run one of the following commands: .. code-block:: shell koan --server=cobbler.example.org --display --profile=name koan --server=cobbler.example.org --display --system=name koan --server=cobbler.example.org --display --image=name Reinstalling existing systems ############################# Using --replace-self will reinstall the existing system the next time you reboot. koan --server=cobbler.example.org --replace-self --profile=name koan --server=cobbler.example.org --replace-self --system=name Additionally, adding the flag --add-reinstall-entry will make it add the entry to grub for reinstallation but will not make it automatically pick that option on the next boot. Also the flag --kexec can be appended, which will launch the installer without needing to reboot. Not all kernels support this option. Installing virtualized systems ############################## Using ``--virt`` will install virtual machines as defined by Cobbler. There are various overrides you can use if not everything in Cobbler is defined as you like it. .. code-block:: shell koan --server=cobbler.example.org --virt --profile=name koan --server=cobbler.example.org --virt --system=name koan --server=cobbler.example.org --virt --image=name Some of the overrides that can be used with --virt are: +-------------------+---------------------------------------+---------------------------+ | Flag | Explanation | Example | +===================+=======================================+===========================+ | ``--virt-name`` | name of virtual machine to create | testmachine | +-------------------+---------------------------------------+---------------------------+ | ``--virt-type`` | forces usage of qemu/xen/vmware | qemu | +-------------------+---------------------------------------+---------------------------+ | ``--virt-bridge`` | name of bridge device | virbr0 | +-------------------+---------------------------------------+---------------------------+ | ``--virt-path`` | overwrite this disk partition | `/dev/sda4` | +-------------------+---------------------------------------+---------------------------+ | ``--virt-path`` | use this directory | `/opt/myimages` | +-------------------+---------------------------------------+---------------------------+ | ``--virt-path`` | use this existing LVM volume | `VolGroup00` | +-------------------+---------------------------------------+---------------------------+ | ``--nogfx`` | do not use VNC graphics (Xen only) | (does not take options) | +-------------------+---------------------------------------+---------------------------+ Nearly all of these variables can also be defined and centrally managed by the Cobbler server. If installing virtual machines in environments without DHCP, use of ``--system`` instead of ``--profile`` is required. Additionally use ``--static-interface=eth0`` to supply which interface to use to supply network information. The installer will boot from this virtual interface. Leaving off ``--static-interface`` will result in an unsuccessful network installation. Configuration management ######################## Using ``--update-config`` will update a system configuration as defined by Cobbler. .. code-block:: shell koan --server=cobbler.example.org --update-config Additionally, adding the flag ``--summary`` will print configuration run stats. Koan passes in the system's FQDN in the background during the configuration request. Cobbler will match this FQDN to a configured system defined by Cobbler. The FQDN (Fully Qualified Domain Name) maps to the system's hostname field. Environment variables ##################### Koan respects the COBBLER_SERVER variable to specify the Cobbler server to use. This is a convenient way to avoid using the ``--server`` option for each command. This variable is set automatically on systems installed via Cobbler, assuming standard kickstart templates are used. If you need to change this on an installed system, edit ``/etc/profile.d/cobbler.{csh,sh}``. Additional ########## Reading the Koan manpage, https://cobbler.github.io or this readthedocs project is highly recommended. Author ###### Michael DeHaan <michael.dehaan AT gmail> Revised by: Enno Gotthold <matrixfueller@gmail.com> 0707010000002D000081A400000000000000000000000160FFEB8C0000024C000000000000000000000000000000000000002300000000koan-3.0.1/docs/reinstallation.rst************** Reinstallation ************** Cobbler's helper program, Koan, can be installed on remote systems. It can then be used to reinstall systems, as well as it's original purpose of installing virtual machines. Usage is as follows: .. code-block:: shell koan --server cobbler.example.com --profile profileName koan --server cobbler.example.com --system systemName Koan will then configure the bootloader to reinstall the system at next boot. This can also be used for OS upgrades with an upgrade kickstart as opposed to a kickstart that specifies a clean install. 0707010000002E000081A400000000000000000000000160FFEB8C0000119A000000000000000000000000000000000000002200000000koan-3.0.1/docs/release-notes.rstRelease Notes for Cobbler 3.0.0 ------------------------------- Enhancements ++++++++++++ * Use new dracut ip option for configuring static interfaces (koan). * Add a whitelist of directories in order to persist a ``cobbler sync``. * Add proxy support for get-loaders, signature update and reposync. * Add initial support for DJBDNS. * Enable external YUM repo mirroring through a proxy server. * DHCP configuration now also supports the per interface gateway setting. * A new interface_type ``BMC`` was added which also can be managed with DHCP. * Yaboot was updated to 1.3.17. * Add ability to have per-profile/per-system ``next_server`` values (#1196). * Add ``--graphics`` option to Koan. * Improved input validation and error handling. * Support ``virtio26`` for generic QEMU fallback in Koan. * Debian network config: add support for tagged vlan only bonding interfaces. * Documentation has been converted into rST and is now included with the source tree. * Integrated pyflakes into the build system and resolved hundreds of issues. * Integrated pep8 (coding style) into the build system and resolved thousands of issues. * Add a new field to the system type ``ipv6_prefix`` (#203). * Minor update to CSS; make better use of screen (tables) (cobbler-web). * Add support for an empty system status. * If ``dns-name`` is specified, set it as DHCP hostname in preference to the ``hostname`` field. * Allow user to choose whether or not to delete item(s) recursively (cobbler-web). * Set ksdevice kernel option to MAC address for ppc systems as bootif is not used by yaboot. * Return to list of snippets/kickstarts when snippet/kickstart is saved (cobbler-web). * Layout in snippet/kickstart edit form has been improved (cobbler-web). * Better handling of copy/remove actions for subprofiles (API and cobbler-web). * Make kickstart selectable from a pulldown list in cobbler-web (#991). Bugfixes ++++++++ * Changed Apache configuration directory in Ubuntu 14.04 (#1208). * build_reporting no longer fails with an empty string in ignorelist (#1248). * Kickstart repo statement, filter invalid values: ``gpgcheck``, ``gpgkey`` and ``enabled`` (#323). * Several improvements to Debian/Ubuntu packaging. * Some class/method names have been changed to make the code more intuitive for developers. * Remove ``root=`` argument in Koan when using grubby and replace-self to avoid booting the current OS. * Exit with an error if the cobblerd executable can't be found (#1108, #1135). * Fix Cobbler sync bug by xmlrpclib returning NoneType object. * Dont send the Puppet environment when system status is empty (#560). * Cobbler-web kept only the most recent interface change (#687). * Fix broken gitdate, gitstamp values in ``/etc/cobbler/version``. * Prevent disappearing profiles after cobblerd restart (#1030). * Add missing icons to cobbler_web/content (#679). * cobbler-ext-nodes was broken with ``mgmt_classes`` defined at the profile level (#790). * Properly name the VLAN interface in the manual page. * Fix wrong address of the Free Software Foundation. * Remove legacy (EL5/6) cruft from the RPM specfile. * Koan: use the print function instead of the print statement. * Minor improvement to LDAP configuration (#217). * Improvements to the unittest framework. * Removed several unused functions from utils. * List of authors is now automagically generated. Upgrade notes +++++++++++++ * Support for LDAP configuration through Koan has been removed. * Support for redhat_management (Spacewalk/Satelite) has been moved to contrib. Users of this functionality should checkout contrib/redhat-management/README. * Monit support has been removed; you really need to use a CMS to manage your services. * Support for remote kickstart templates and files been removed (eg. kickstart=http://). * All object names are now validated like that of the system object. * The use of ``parent`` and ``distro`` on subprofiles are now mutually exclusive. * Support for s390/s390x has been removed. * Support for ia64 (Itanium) has been removed. * Support for the MySQL backend has been removed. * Support for deprecated fieldnames (``subnet``, ``bonding_master``, ``bonding``) has been removed. * Cobbler now requires python 2.7 and Koan now requires python 2.6. * Red Hat specific default kernel options have been removed from the settings file. * Support for Func integration has been moved to contrib. Users of this functionality should checkout contrib/func/README. * Deprecated Koan LiveCD: moved to contrib. 0707010000002F000081A400000000000000000000000160FFEB8C0000111A000000000000000000000000000000000000002D00000000koan-3.0.1/docs/virtual-networking-setup.rst************************ Virtual Networking Setup ************************ Notice ###### For Xen and QEMU/KVM virtual machines to be able to get outside access they will need to have a virtual bridge configured on the virtual host. (If you're using VMware this page won't apply to you) While "virbr0" should automatically be set up if you are using a newer libvirt, it's not a real bridge and you won't be able to contact your guests from outside -- it's a private network. So you most likely do NOT want to use virbr0 if you are doing anything useful. "xenbr0" if you have that, is fine to use. The following instructions show about how to set up bridging manually which must be done on a host to make things work as you would expect. Remember if you have a "xenbr0", you can use that though -- it's a real bridge. If you want something more specific you can still create your own. xenbr0 is created in most versions of RHEL by xend startup. Networking ########## Virtualization networking in Koan uses "bridged" mode. This is so that guests by default can be connected to from the outside world, which is very important for them to be able to do useful things. If a network bridge already exists, Koan will be able to use that, though in some cases, you'll have to create your own bridge in order to get Koan to work. You do this by modifying the network configuration on your virtual host, and then using the Koan parameter ``--virt-bridge=bridgename``. (As we mentioned, if you use virbr0, it's a fake bridge, so be aware you won't be able to ssh into your guests... However Koan can use that if you REALLY want to) Basics ###### The configuration in this section is adapted from `here <http://watzmann.net/blog/2007/04/networking-with-kvm-and-libvirt.html>`_. We're going to be ignoring the parts in that article about using virt-install as we want to use Koan -- we want to make our virtualized configurations be managed server side, by Cobbler -- and to take advantage of things that Cobbler provides for us, like syslog setup, templating, remote profile browsing, etc. So here's the short rundown of what you need to do to create a bridge if you do not already have one. Once you do this once for each guest, you're set up -- so if you have bare metal profiles for Cobbler set up, it may make sense to make those profiles set up your bridge at install time as well. Set up ``/etc/sysconfig/network-scripts/ifcfg-peth0`` to define your physical NIC: DEVICE=peth0 ONBOOT=yes BRIDGE=eth0 HWADDR=XX:XX:XX:XX:XX:XX Substitute the X's in HWADDR for the mac address of the NIC you'd get from ``/sbin/ifconfig`` Now set up the bridge interface: ``/sbin/sysconfig/network-scripts/ifcfg-eth0``: DEVICE=eth0 BOOTPROTO=dhcp ONBOOT=yes TYPE=Bridge As the above link recommends, "You also want to add an iptables rule that allows forwarding of packets on the bridged physical NIC (otherwise DHCP from your guests won't work)". # service iptables start # iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT # service iptables save Alternatively, you could also disable iptables (at your own risk). Now you should be able to use Koan as follows: .. code-block:: shell koan --server=bootserver.example.org --profile=RHEL5-i386 --virt To force a specific choice, you can use ``--virt-bridge`` and specify the name of any bridge you like. Note that this must be a /real/ bridge, and not a physical interface. If you use a physical interface things will not work. .. code-block:: shell koan --server=bootserver.example.org --profile=RHEL5-i386 --virt --virt-bridge=peth0 Hopefully that helps address some of the basics around virtual networking setup. The above instructions work for both Xen and QEMU/KVM. (If you encounter problems with the above please bring them up on the gitter-chat list) Network Manager ############### At the time of writing this (Fedora 10), Network Manager does not support bridging. You should also set NM_MANAGED=No in the configuration file for your physical interface to disable NetworkManager on hosts that use it. (This has the side effect of tricking firefox into offline mode on startup, but we are hopefully talking about servers here... if this bothers you, go into `about:config` and search for networkmanager. Turn off the firefox network manager toolkit). 07070100000030000081A400000000000000000000000160FFEB8C00000A2C000000000000000000000000000000000000001B00000000koan-3.0.1/docs/vmware.rst*********************** VMWare support for Koan *********************** One goal of Cobbler is to abstract out all your installation types. Basically the idea is create a profile one, use it for PXE, reinstallations (``--replace-self``) and virtualized machines all at once. Installing ########## To get this working, you will need to be using Cobbler to host your PXE infrastructure. You can then, using special Koan syntax (one single command line invocation), install a Cobbler profile /inside/ VMware, pulling the RAM and Disk storage parameters from Cobbler. This allows you to treat VMware just as another installation type... The same kickstart and parameters you use to install inside your bare metal machines and other virtualization types can be used for VMware in very similar ways. Setup ##### First, grab yourself some caffeine. Second, set up VMware as normal. (As of the time of writing this, we support VMware server and workstation, and there is a patch being worked on to support nearly all VMware variants) - Install the latest VMware server. At the time of testing, I managed to get VMware to work on EL 5. - Install the latest any-any patch to make VMware actually work on said platform - run /usr/bin/vmware-config.pl as appropriate - enter your free serial number when requested - make sure the VMware service is enabled and started Then do some minimal Cobbler setup: - add Cobbler profiles and Cobbler system records to taste - Cobbler must be set up to be your PXE infrastructure, which it should be doing already hopefully - "cobbler system add" a new system and specify a mac address in the range of 00:50:56:00:00:00 to 00:50:56:3F:FF:FF (VMWare's allowed range). Note that MAC addresses outside that range will not work, and you cannot skip the step of creating them in that range. Unlike Xen/KVM they are also not auto-generated by Koan... The reason for this is, without the mapping in Cobbler, PXE control over what profile is installed is meaningless. Then, on the guest OS, run the following: .. code-block:: shell koan --server=cobbler.example.org --system=foosball --virt --virt-type=vmware What Happens ############ Koan will ask Cobbler for the RAM and Disk needs associated with "foosball", or rather, the profile associated with "foosball". It will then automatically create your VMware configuration file (vmx) and disk image, and set that image up so that the rest of the installation happens over PXE. Thus the installation is in tandem between PXE and the VMware libraries, though all of the profile data is still managed by Cobbler. 07070100000031000041ED00000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001000000000koan-3.0.1/koan07070100000032000081A400000000000000000000000160FFEB8C000012AD000000000000000000000000000000000000001500000000koan-3.0.1/koan.spec# # RPM spec file for Koan # # Supported/tested build targets: # - Fedora: 30, 31, Rawhide # - CentOS + EPEL: 7, 8 # - SLE: 11sp4, 12sp3, 15sp1 # - OpenSuSE: Leap 15.1, Tumbleweed # - Debian: 9, 10 # - Ubuntu: 16.04, 18.04 # # If it doesn't build on the Open Build Service (OBS) it's a bug. # %if 0%{?suse_version} && 0%{?suse_version} < 1500 %bcond_without use_python2 %else %bcond_with use_python2 %endif # If they aren't provided by a system installed macro, define them %{!?__python2: %global __python2 /usr/bin/python2} %{!?__python3: %global __python3 /usr/bin/python3} %if "%{_vendor}" == "debbuild" %global pyinstflags --no-compile -O0 %global pytargetflags --install-layout=deb %global develsuffix dev %else %global pyinstflags -O1 %global pytargetflags %{nil} %global develsuffix devel %endif %if %{with use_python2} %global __python %{__python2} %global py_shbang_opts %{py2_shbang_opts} %global python_pkgversion %{nil} %else %global __python %{__python3} %global py_shbang_opts %{py3_shbang_opts} %{!?python3_pkgversion: %global python3_pkgversion 3} %global python_pkgversion %{python3_pkgversion} %endif %{!?py_build: %global py_build CFLAGS="%{optflags}" %{__python} setup.py build} %{!?py_install: %global py_install %{__python} setup.py install %{?pyinstflags} --skip-build --root %{buildroot} --prefix=%{_prefix} %{?pytargetflags}} # Always override this definition to unbreak SUSE distributions %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") Name: koan Version: 2.9.0 Release: 1%{?dist} Summary: Kickstart over a network %if "%{_vendor}" == "debbuild" Packager: Cobbler Developers <cobbler@lists.fedorahosted.org> Group: admin %else Group: Development/Libraries %endif License: GPL-2.0-or-later URL: https://github.com/cobbler/koan Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz %if 0%{?suse_version} && 0%{?suse_version} < 1315 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot %else BuildArch: noarch %endif Requires: python%{python_pkgversion}-koan = %{version}-%{release} %description Koan stands for kickstart-over-a-network and allows for both network installation of new virtualized guests and reinstallation of an existing system. For use with a boot-server configured with Cobbler. %package -n python%{python_pkgversion}-koan Summary: koan python%{python_pkgversion} module %if 0%{?suse_version} && 0%{?suse_version} < 1315 Group: Development/Libraries/Python %endif %{?python_provide:%python_provide python%{python_pkgversion}-koan} BuildRequires: python%{python_pkgversion}-%{develsuffix} BuildRequires: python%{python_pkgversion}-setuptools %if 0%{?rhel} # We need these to build this properly, and OBS doesn't pull them in by default for EPEL BuildRequires: epel-rpm-macros %endif %{?python_enable_dependency_generator} %if ! (%{defined python_enable_dependency_generator} || %{defined python_disable_dependency_generator}) Requires: python%{python_pkgversion}-distro Requires: python%{python_pkgversion}-netifaces %if 0%{?suse_version} # SUSE distributions have messed up naming of this module %if 0%{?suse_version} < 1500 Requires: libvirt-python%{python_pkgversion} %else Requires: python%{python_pkgversion}-libvirt-python %endif %else Requires: python%{python_pkgversion}-libvirt %endif %endif %if "%{_vendor}" == "debbuild" Requires: virtinst %else Requires: virt-install %endif %description -n python%{python_pkgversion}-koan This package provides the Python module code for Koan. %prep %setup -q %if 0%{?fedora}%{?rhel} pathfix.py -pni "%{__python} %{py_shbang_opts}" bin %endif %build %py_build %install %py_install %if 0%{?suse_version} && 0%{?suse_version} < 1315 %clean rm -rf %{buildroot} %endif %files %if 0%{?suse_version} && 0%{?suse_version} < 1315 %{!?_licensedir:%global license %doc} %defattr(-,root,root,-) %endif %license COPYING %doc README.md %{_bindir}/koan %{_bindir}/cobbler-register %files -n python%{python_pkgversion}-koan %if 0%{?suse_version} && 0%{?suse_version} < 1315 %{!?_licensedir:%global license %doc} %defattr(-,root,root,-) %endif %license COPYING %{python_sitelib}/koan* %if "%{_vendor}" == "debbuild" %post -n python%{python_pkgversion}-koan # Do late-stage bytecompilation, per debian policy py%{python_pkgversion}compile -p python%{python_pkgversion}-koan %preun -n python%{python_pkgversion}-koan # Ensure all __pycache__ files are deleted, per debian policy py%{python_pkgversion}clean -p python%{python_pkgversion}-koan %endif %changelog * Sun Nov 24 2019 Neal Gompa <ngompa13@gmail.com> - Initial rewrite of packaging 07070100000033000081A400000000000000000000000160FFEB8C00000000000000000000000000000000000000000000001C00000000koan-3.0.1/koan/__init__.py07070100000034000081ED00000000000000000000000160FFEB8C00012D6D000000000000000000000000000000000000001700000000koan-3.0.1/koan/app.py""" koan = kickstart over a network a tool for network provisioning of virtualization (xen,kvm/qemu,vmware) and network re-provisioning of existing Linux systems. used with 'cobbler'. see manpage for usage. Copyright 2006-2008 Red Hat, Inc and Others. Michael DeHaan <michael.dehaan AT gmail> 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 """ import errno import os import random import re import shlex import shutil import socket import subprocess import sys import time import traceback from optparse import OptionParser from . import configurator from . import utils from .cexceptions import InfoException COBBLER_REQUIRED = 1.300 KOAN_CONF_DIR = '/var/lib/koan/config/' """ koan --virt [--profile=webserver|--system=name] --server=hostname koan --replace-self --profile=foo --server=hostname [--kexec] """ DISPLAY_PARAMS = [ "name", "distro", "profile", "autoinst", "ks_meta", "install_tree", "kernel", "initrd", "netboot_enabled", "kernel_options", "repos", "virt_ram", "virt_disk", "virt_disk_driver", "virt_type", "virt_path", "virt_auto_boot", "virt_pxe_boot", ] def main(): """ Command line stuff... """ try: utils.setupLogging("koan") except: # most likely running RHEL3, where we don't need virt logging anyway pass p = OptionParser() p.add_option( "-k", "--kopts", dest="kopts_override", help="append additional kernel options" ) p.add_option( "-l", "--list", dest="list_items", help="lists remote items (EX: profiles, systems, or images)" ) p.add_option( "-v", "--virt", dest="is_virt", action="store_true", help="install new virtual guest" ) p.add_option( "-u", "--update-files", dest="is_update_files", action="store_true", help="update templated files from cobbler config management" ) p.add_option( "-c", "--update-config", dest="is_update_config", action="store_true", help="update system configuration from cobbler config management" ) p.add_option( "", "--summary", dest="summary", action="store_true", help="print configuration run stats" ) p.add_option( "-V", "--virt-name", dest="virt_name", help="use this name for the virtual guest" ) p.add_option( "-r", "--replace-self", dest="is_replace", action="store_true", help="reinstall this host at next reboot" ) p.add_option( "-D", "--display", dest="is_display", action="store_true", help="display the configuration stored in cobbler for the given object" ) p.add_option( "-p", "--profile", dest="profile", help="use this cobbler profile" ) p.add_option( "-y", "--system", dest="system", help="use this cobbler system" ) p.add_option( "-i", "--image", dest="image", help="use this cobbler image" ) p.add_option( "-s", "--server", dest="server", default=os.environ.get("COBBLER_SERVER", ""), help="attach to this cobbler server" ) p.add_option( "-S", "--static-interface", dest="static_interface", help="use static network configuration from this interface while installing" ) p.add_option( "-t", "--port", dest="port", help="cobbler port (default 80)" ) p.add_option( "-w", "--vm-poll", dest="should_poll", action="store_true", help="for xen/qemu/KVM, poll & restart the VM after the install is done" ) p.add_option( "-P", "--virt-path", dest="virt_path", help="override virt install location" ) p.add_option( "", "--force-path", dest="force_path", action="store_true", help="Force overwrite of virt install location" ) p.add_option( "-T", "--virt-type", dest="virt_type", help="override virt install type" ) p.add_option( "-B", "--virt-bridge", dest="virt_bridge", help="override virt bridge" ) p.add_option( "-n", "--nogfx", action="store_true", dest="no_gfx", help="disable Xen graphics (xenpv,xenfv)" ) p.add_option( "-g", "--graphics", dest="gfx_type", default="vnc", help="specify the graphics type: vnc, sdl, spice, none" ) p.add_option( "", "--virt-auto-boot", action="store_true", dest="virt_auto_boot", help="set VM for autoboot" ) p.add_option( "", "--virt-pxe-boot", action="store_true", dest="virt_pxe_boot", help="PXE boot for installation override" ) p.add_option( "", "--add-reinstall-entry", dest="add_reinstall_entry", action="store_true", help="when used with --replace-self, just add entry to grub, \ do not make it the default" ) p.add_option( "-C", "--livecd", dest="live_cd", action="store_true", help="used by the custom livecd only, not for humans" ) p.add_option( "", "--kexec", dest="use_kexec", action="store_true", help="Instead of writing a new bootloader config when using --replace-self, just kexec the new kernel and " "initrd " ) p.add_option( "", "--no-copy-default", dest="no_copy_default", action="store_true", help="Do not copy the kernel args from the default kernel entry when using --replace-self" ) p.add_option( "", "--embed", dest="embed_autoinst", action="store_true", help="When used with --replace-self, embed the autoinst in the initrd to overcome potential DHCP timeout " "issues. (seldom needed) " ) p.add_option( "", "--qemu-disk-type", dest="qemu_disk_type", help="when used with --virt_type=qemu, add select of disk driver types: ide,scsi,virtio" ) p.add_option( "", "--qemu-net-type", dest="qemu_net_type", help="when used with --virt_type=qemu, select type of network device to use: e1000, ne2k_pci, pcnet, rtl8139, " "virtio " ) p.add_option( "", "--qemu-machine-type", dest="qemu_machine_type", help="when used with --virt_type=qemu, select type of machine type to emulate: pc, pc-1.0, pc-0.15" ) p.add_option( "", "--wait", # default to 0 for koan backwards compatibility dest="wait", type='int', default=0, help="pass the --wait=<INT> argument to virt-install" ) p.add_option( "", "--noreboot", # default to False for koan backwards compatibility dest="noreboot", default=False, action="store_true", help="pass the --noreboot argument to virt-install" ) p.add_option( "", "--import", # default to False for koan backwards compatibility dest="osimport", default=False, action="store_true", help="pass the --import argument to virt-install" ) (options, args) = p.parse_args() try: k = Koan() k.list_items = options.list_items k.server = options.server k.is_virt = options.is_virt k.is_update_files = options.is_update_files k.is_update_config = options.is_update_config k.summary = options.summary k.is_replace = options.is_replace k.is_display = options.is_display k.profile = options.profile k.system = options.system k.image = options.image k.live_cd = options.live_cd k.virt_path = options.virt_path k.force_path = options.force_path k.virt_type = options.virt_type k.virt_bridge = options.virt_bridge k.add_reinstall_entry = options.add_reinstall_entry k.kopts_override = options.kopts_override k.static_interface = options.static_interface k.use_kexec = options.use_kexec k.no_copy_default = options.no_copy_default k.should_poll = options.should_poll k.embed_autoinst = options.embed_autoinst k.virt_auto_boot = options.virt_auto_boot k.virt_pxe_boot = options.virt_pxe_boot k.qemu_disk_type = options.qemu_disk_type k.qemu_net_type = options.qemu_net_type k.qemu_machine_type = options.qemu_machine_type k.virtinstall_wait = options.wait k.virtinstall_noreboot = options.noreboot k.virtinstall_osimport = options.osimport if options.virt_name is not None: k.virt_name = options.virt_name if options.port is not None: k.port = options.port if options.gfx_type is not None and options.no_gfx is not None: raise InfoException("Error: cannot specify both -n|--no_gfx and -g|--graphics") if options.gfx_type == "none" or options.no_gfx is not None: k.gfx_type = None else: k.gfx_type = options.gfx_type k.run() except Exception as e: (xa, xb, tb) = sys.exc_info() try: getattr(e, "from_koan") print(str(e)[1:-1]) # nice exception, no traceback needed except: print(xa) print(xb) print("".join(traceback.format_list(traceback.extract_tb(tb)))) return 1 return 0 class Koan: def __init__(self): """ Constructor. Arguments will be filled in by optparse... """ self.server = None self.system = None self.profile = None self.image = None self.live_cd = None self.list_items = None self.list_profiles = None self.list_systems = None self.is_virt = None self.is_update_files = None self.is_update_config = None self.summary = None self.is_replace = None self.is_display = None self.port = None self.static_interface = None self.virt_name = None self.virt_type = None self.virt_path = None self.virt_bridge = None self.force_path = None self.qemu_disk_type = None self.qemu_net_type = None self.qemu_machine_type = None self.virt_auto_boot = None self.virt_pxe_boot = None self.virtinstall_wait = None self.virtinstall_noreboot = None self.virtinstall_osimport = None self.gfx_type = None self.add_reinstall_entry = None self.kopts_override = None self.use_kexec = None self.should_poll = None self.embed_autoinst = None # This option adds the --copy-default argument to /sbin/grubby # which uses the default boot entry in the grub.conf # as template for the new entry being added to that file. # look at /sbin/grubby --help for more info self.no_copy_default = None def run(self): """ koan's main function... """ # we can get the info we need from either the cobbler server # or a autoinst file if self.server is None: raise InfoException("no server specified") # check to see that exclusive arguments weren't used together found = 0 for x in (self.is_virt, self.is_replace, self.is_update_files, self.is_display, self.list_items, self.is_update_config): if x: found = found + 1 if found != 1: raise InfoException( "choose: --virt, --replace-self, --update-files, --list=what, or --display" ) # This set of options are only valid with --server if not self.server or self.server == "": if self.list_items or self.profile or self.system or self.port: raise InfoException("--server is required") self.xmlrpc_server = utils.connect_to_server( server=self.server, port=self.port) if self.list_items: self.list(self.list_items) return if not os.getuid() == 0: if self.is_virt: print("warning: running as non root") elif not self.is_display: print("this operation requires root access") return 3 # if both --profile and --system were ommitted, autodiscover if self.is_virt: if self.profile is None and self.system is None and self.image is None: raise InfoException( "must specify --profile, --system, or --image") else: if self.profile is None and self.system is None and self.image is None: self.system = self.autodetect_system( allow_interactive=self.live_cd) if self.system is None: while self.profile is None: self.profile = self.ask_profile() # if --virt-type was specified and invalid, then fail if self.virt_type is not None: self.virt_type = self.virt_type.lower() if self.virt_type not in ["qemu", "xenpv", "xenfv", "xen", "vmware", "vmwarew", "auto", "kvm"]: if self.virt_type == "xen": self.virt_type = "xenpv" raise InfoException( "--virt-type should be qemu, xenpv, xenfv, vmware, vmwarew, kvm, or auto") # if --qemu-disk-type was called without --virt-type=qemu, then fail if self.qemu_disk_type is not None: self.qemu_disk_type = self.qemu_disk_type.lower() if self.virt_type not in ["qemu", "auto", "kvm"]: raise InfoException( "--qemu-disk-type must use with --virt-type=qemu") # if --qemu-net-type was called without --virt-type=qemu, then fail if self.qemu_net_type is not None: self.qemu_net_type = self.qemu_net_type.lower() if self.virt_type not in ["qemu", "auto", "kvm"]: raise InfoException( "--qemu-net-type must use with --virt-type=qemu") # if --qemu-machine-type was called without --virt-type=qemu, then fail if self.qemu_machine_type is not None: self.qemu_machine_type = self.qemu_machine_type.lower() if self.virt_type not in ["qemu", "auto", "kvm"]: raise InfoException( "--qemu-machine-type must use with --virt-type=qemu") # if --static-interface and --profile was called together, then fail if self.static_interface is not None and self.profile is not None: raise InfoException( "--static-interface option is incompatible with --profile option use --system instead") # perform one of three key operations if self.is_virt: self.virt() elif self.is_replace: if self.use_kexec: self.kexec_replace() else: self.replace() elif self.is_update_files: self.update_files() elif self.is_update_config: self.update_config() else: self.display() def ask_profile(self): """ Used by the live CD mode, if the system can not be auto-discovered, show a list of available profiles and ask the user what they want to install. """ # FIXME: use a TUI library to make this more presentable. try: available_profiles = self.xmlrpc_server.get_profiles() except: traceback.print_exc() self.connect_fail() print("\n- which profile to install?\n") for x in available_profiles: print("%s" % x["name"]) sys.stdout.write("\n?>") data = sys.stdin.readline().strip() for x in available_profiles: print("comp (%s,%s)" % (x["name"], data)) if x["name"] == data: return data return None def autodetect_system(self, allow_interactive=False): """ Determine the name of the cobbler system record that matches this MAC address. """ systems = self.get_data("systems") my_netinfo = utils.get_network_info() my_interfaces = my_netinfo.keys() mac_criteria = [] ip_criteria = [] for my_interface in my_interfaces: mac_criteria.append( my_netinfo[my_interface]["mac_address"].upper()) ip_criteria.append(my_netinfo[my_interface]["ip_address"]) detected_systems = [] systems = self.get_data("systems") for system in systems: obj_name = system["name"] for (obj_iname, obj_interface) in system['interfaces'].items(): mac = obj_interface["mac_address"].upper() ip = obj_interface["ip_address"].upper() for my_mac in mac_criteria: if mac == my_mac: detected_systems.append(obj_name) for my_ip in ip_criteria: if ip == my_ip: detected_systems.append(obj_name) detected_systems = utils.uniqify(detected_systems) if len(detected_systems) > 1: raise InfoException("Error: Multiple systems matched") elif len(detected_systems) == 0: if not allow_interactive: mac_criteria = utils.uniqify(mac_criteria, purge="?") ip_criteria = utils.uniqify(ip_criteria, purge="?") raise InfoException( "Error: Could not find a matching system with MACs: %s or IPs: %s" % (",".join(mac_criteria), ",".join(ip_criteria))) else: return None elif len(detected_systems) == 1: print("- Auto detected: %s" % detected_systems[0]) return detected_systems[0] def safe_load(self, hashv, primary_key, alternate_key=None, default=None): if primary_key in hashv: return hashv[primary_key] elif alternate_key is not None and alternate_key in hashv: return hashv[alternate_key] else: return default def net_install(self, after_download): """ Actually kicks off downloads and auto-ks or virt installs """ # initialise the profile, from the server if any if self.profile: profile_data = self.get_data("profile", self.profile) elif self.system: profile_data = self.get_data("system", self.system) elif self.image: profile_data = self.get_data("image", self.image) else: # shouldn't end up here, right? profile_data = {} if profile_data.get("autoinst", "") == "": # Fall back to kickstart from cobbler < 3.X if profile_data.get("kickstart", "") != "": profile_data["autoinst"] = profile_data["kickstart"] if "autoinst" in profile_data: # fix URLs if profile_data["autoinst"][0] == "/": if not self.system: profile_data["autoinst"] = "http://%s/cblr/svc/op/ks/profile/%s" % ( profile_data['http_server'], profile_data['name']) else: profile_data["autoinst"] = "http://%s/cblr/svc/op/ks/system/%s" % ( profile_data['http_server'], profile_data['name']) # If breed is ubuntu/debian we need to source the install tree differently # as preseeds are used instead of kickstarts. if profile_data["breed"] in ["ubuntu", "debian", "suse"]: self.get_install_tree_from_profile_data(profile_data) else: # find_autoinst source tree in the autoinst file self.get_install_tree_from_autoinst(profile_data) # if we found an install_tree, and we don't have a kernel or initrd # use the ones in the install_tree if self.safe_load(profile_data, "install_tree"): if not self.safe_load(profile_data, "kernel"): profile_data["kernel"] = profile_data["install_tree"] + "/images/pxeboot/vmlinuz" if not self.safe_load(profile_data, "initrd"): profile_data["initrd"] = profile_data["install_tree"] + "/images/pxeboot/initrd.img" # find the correct file download location if not self.is_virt: download = "/boot" else: # ensure we have a good virt type choice and know where # to download the kernel/initrd if self.virt_type is None: self.virt_type = self.safe_load( profile_data, 'virt_type', default=None) if self.virt_type is None or self.virt_type == "": self.virt_type = "auto" # if virt type is auto, reset it to a value we can actually use if self.virt_type == "auto": if profile_data.get("xml_file", "") != "": raise InfoException( "xmlfile based installations are not supported") elif "file" in profile_data: print("- ISO or Image based installation, always uses " "--virt-type=qemu") self.virt_type = "qemu" else: # FIXME: auto never selects vmware, maybe it should if we # find it? cmd = subprocess.Popen( "/bin/uname -r", stdout=subprocess.PIPE, shell=True) uname_str = cmd.communicate()[0].decode() if uname_str.find("xen") != -1: self.virt_type = "xenpv" elif os.path.exists("/usr/bin/qemu-img"): self.virt_type = "qemu" else: # assume Xen, we'll check to see if virt-type is # really usable later. raise InfoException( "Not running a Xen kernel and qemu is not installed") print("- no virt-type specified, auto-selecting %s" % self.virt_type) # now that we've figured out our virt-type, let's see if it is really usable # rather than showing obscure error messages from Xen to the user # :) if self.virt_type in ["xenpv", "xenfv"]: cmd = subprocess.Popen( "uname -r", stdout=subprocess.PIPE, shell=True) uname_str = cmd.communicate()[0].decode() # correct kernel on dom0? if uname_str < "2.6.37" and uname_str.find("xen") == -1: raise InfoException( "kernel >= 2.6.37 or kernel-xen needs to be in use") # xend installed? if not os.path.exists("/usr/sbin/xend"): raise InfoException("xen package needs to be installed") # xend running? rc = subprocess.call( "/usr/sbin/xend status", stderr=None, stdout=None, shell=True) if rc != 0: raise InfoException("xend needs to be started") # for qemu if self.virt_type in ["qemu", "kvm"]: # qemu package installed? if not os.path.exists("/usr/bin/qemu-img"): raise InfoException("qemu package needs to be installed") # for vmware if self.virt_type == "vmware" or self.virt_type == "vmwarew": # FIXME: if any vmware specific checks are required (for deps) # do them here. pass if self.virt_type == "virt-image": if not os.path.exists("/usr/bin/virt-image"): raise InfoException( "virt-image not present, downlevel virt-install package?") # for both virt types if os.path.exists("/etc/rc.d/init.d/libvirtd"): rc = subprocess.call( "/sbin/service libvirtd status", stdout=None, shell=True) if rc != 0: # libvirt running? raise InfoException("libvirtd needs to be running") if self.virt_type in ["xenpv"]: # we need to fetch the kernel/initrd to do this download = "/var/lib/xen" elif self.virt_type in ["xenfv", "vmware", "vmwarew"]: # we are downloading sufficient metadata to initiate PXE, no # D/L needed download = None else: # qemu # fullvirt, can use set_location in virtinst library, no D/L # needed yet download = None # download required files if not self.is_display and download is not None: self.get_distro_files(profile_data, download) # perform specified action after_download(self, profile_data) def get_install_tree_from_autoinst(self, profile_data): """ Scan the autoinst configuration for either a "url" or "nfs" command take the install_tree url from that """ try: if profile_data["autoinst"][:4] == "http": if not self.system: url_fmt = "http://%s/cblr/svc/op/ks/profile/%s" else: url_fmt = "http://%s/cblr/svc/op/ks/system/%s" url = url_fmt % (self.server, profile_data['name']) else: url = profile_data["autoinst"] lines = utils.urlread(url).decode().splitlines() method_re = re.compile(r"(?P<urlcmd>\s*url\s.*)|(?P<nfscmd>\s*nfs\s.*)") url_parser = OptionParser() url_parser.add_option("--url", dest="url") url_parser.add_option("--proxy", dest="proxy") nfs_parser = OptionParser() nfs_parser.add_option("--dir", dest="dir") nfs_parser.add_option("--server", dest="server") for line in lines: match = method_re.match(line) if match: cmd = match.group("urlcmd") if cmd: (options, args) = url_parser.parse_args( shlex.split(cmd)[1:]) profile_data["install_tree"] = options.url break cmd = match.group("nfscmd") if cmd: (options, args) = nfs_parser.parse_args( shlex.split(cmd)[1:]) profile_data[ "install_tree"] = "nfs://%s:%s" % (options.server, options.dir) break if self.safe_load(profile_data, "install_tree"): print("install_tree:", profile_data["install_tree"]) else: print("warning: autoinst found but no install_tree found") except: # unstable to download the autoinst, however this might not # be an error. For instance, xen FV installations of non # autoinst OS's... pass def get_install_tree_from_profile_data(self, profile_data): """ Split ks_meta to obtain the tree path. Generate the install_tree using the http_server and the tree obtained from splitting ks_meta """ try: tree = profile_data["ks_meta"].split() # Ensure we only take the tree in case ks_meta args are passed # First check for tree= in ks_meta arguments meta_re = re.compile('tree=') tree_found = '' for entry in tree: if meta_re.match(entry): tree_found = entry.split("=")[-1] break if tree_found == '': # assume tree information as first argument tree = tree.split()[0] else: tree = tree_found tree_re = re.compile('(https?|ftp|nfs):') # Next check for installation tree on remote server if tree_re.match(tree): tree = tree.replace( "@@http_server@@", profile_data["http_server"]) profile_data["install_tree"] = tree else: # Now take the first parameter as the local path profile_data["install_tree"] = "http://" + \ profile_data["http_server"] + tree if self.safe_load(profile_data, "install_tree"): print("install_tree:", profile_data["install_tree"]) else: print("warning: autoinst found but no install_tree found") except: if profile_data["breed"] == "suse": options = profile_data["kernel_options"].split(" ") for opt in options: if opt.startswith("install="): profile_data["install_tree"] = opt.replace("install=", "") break else: pass def list(self, what): if what not in ["images", "profiles", "systems", "distros", "repos"]: raise InfoException("koan does not know how to list that") data = self.get_data(what) for x in data: if "name" in x: print(x["name"]) return True def display(self): def after_download(self, profile_data): for x in DISPLAY_PARAMS: if x in profile_data: value = profile_data[x] if x == 'kernel_options': value = self.calc_kernel_args(profile_data) print("%20s : %s" % (x, value)) return self.net_install(after_download) def virt(self): """ Handle virt provisioning. """ def after_download(self, profile_data): self.virt_net_install(profile_data) return self.net_install(after_download) def update_files(self): """ Contact the cobbler server and get any config-management files in cobbler that we are providing to nodes. Basically this turns cobbler into a lighweight configuration management system for folks who are not needing a more complex CMS. Read more at: https://github.com/cobbler/cobbler/wiki/Built-in-configuration-management """ # FIXME: make this a utils.py function if self.profile: profile_data = self.get_data("profile", self.profile) elif self.system: profile_data = self.get_data("system", self.system) elif self.image: profile_data = self.get_data("image", self.image) else: # shouldn't end up here, right? profile_data = {} # BOOKMARK template_files = profile_data["template_files"] template_files = utils.input_string_or_dict(template_files) template_keys = template_files.keys() print("- template map: %s" % template_files) print("- processing for files to download...") for src in template_keys: dest = template_files[src] save_as = dest dest = dest.replace("_", "__") dest = dest.replace("/", "_") if not save_as.startswith("/"): # this is a file in the template system that is not to be # downloaded continue print("- file: %s" % save_as) pattern = "http://%s/cblr/svc/op/template/%s/%s/path/%s" if "interfaces" in profile_data: url = pattern % ( profile_data["http_server"], "system", profile_data["name"], dest) else: url = pattern % ( profile_data["http_server"], "profile", profile_data["name"], dest) if not os.path.exists(os.path.dirname(save_as)): os.makedirs(os.path.dirname(save_as)) cmd = ["/usr/bin/curl", url, "--output ", save_as] utils.subprocess_call(cmd) return True def update_config(self): """ Contact the cobbler server and update the system configuration using cobbler's built-in configuration management. Configs are based on a combination of mgmt-classes assigned to the system, profile, and distro. """ hostname = socket.gethostname() server = self.xmlrpc_server try: config = server.get_config_data(hostname) except: traceback.print_exc() self.connect_fail() default_config_filename = 'localconfig.json' node_config_data = KOAN_CONF_DIR + default_config_filename if os.path.isfile(node_config_data): timestamp = utils.generate_timestamp() old_node_config_data = "".join( (KOAN_CONF_DIR, timestamp, "_", default_config_filename) ) shutil.copyfile(node_config_data, old_node_config_data) f = open(node_config_data, 'w') f.write(config) f.close() print("- Starting configuration run for %s" % (hostname)) runtime_start = time.time() configure = configurator.KoanConfigure(config) stats = configure.run() runtime_end = time.time() if self.summary: pstats = ( stats["pkg"]['nsync'], stats["pkg"]['osync'], stats["pkg"]['fail'], stats["pkg"]['runtime']) dstats = ( stats["dir"]['nsync'], stats["dir"]['osync'], stats["dir"]['fail'], stats["dir"]['runtime']) fstats = ( stats["files"]['nsync'], stats["files"]['osync'], stats["files"]['fail'], stats["files"]['runtime']) nsync = pstats[0] + dstats[0] + fstats[0] osync = pstats[1] + dstats[1] + fstats[1] fail = pstats[2] + dstats[2] + fstats[2] total_resources = (nsync + osync + fail) total_runtime = (runtime_end - runtime_start) print('') print("\tResource Report") print("\t-------------------------") print("\t In Sync: %d" % nsync) print("\tOut of Sync: %d" % osync) print("\t Fail: %d" % fail) print("\t-------------------------") print("\tTotal Resources: %d" % total_resources) print("\t Total Runtime: %.02f" % total_runtime) for status in ["repos_status"]: if status in stats: print('') print("\t%s" % status) print("\t-------------------------") print("\t%s" % stats[status]) print("\t-------------------------") print('') print("\tResource |In Sync|OO Sync|Failed|Runtime") print("\t----------------------------------------") print("\t Packages: %d %d %d %.02f" % pstats) print("\t Directories: %d %d %d %.02f" % dstats) print("\t Files: %d %d %d %.02f" % fstats) print('') def kexec_replace(self): """ Prepare to morph existing system by downloading new kernel and initrd and preparing kexec to execute them. Allow caller to do final 'kexec -e' invocation; this allows modules such as network drivers to be unloaded (for cases where an immediate kexec would leave the driver in an invalid state. """ def after_download(self, profile_data): k_args = self.calc_kernel_args(profile_data) autoinst = self.safe_load(profile_data, 'autoinst') arch = self.safe_load(profile_data, 'arch') (make, version) = utils.os_release() if (make == "centos" and version < 7) or (make == "redhat" and version < 7) or ( make == "fedora" and version < 10) or (make == "suse"): # embed the initrd in the autoinst file because of libdhcp and/or pump # needing the help due to some DHCP timeout potential in some certain # network configs. if self.embed_autoinst: self.build_initrd( self.safe_load(profile_data, 'initrd_local'), autoinst, profile_data ) # Validate kernel argument length (limit depends on architecture -- # see asm-*/setup.h). For example: # asm-i386/setup.h:#define COMMAND_LINE_SIZE 256 # asm-powerpc/setup.h:#define COMMAND_LINE_SIZE 512 # asm-x86_64/setup.h:#define COMMAND_LINE_SIZE 256 # arch/x86/include/asm/setup.h:#define COMMAND_LINE_SIZE 2048 if arch.startswith("ppc"): if len(k_args) > 511: raise InfoException( "Kernel options are too long, 512 chars exceeded: %s" % k_args) elif len(k_args) > 2048: raise InfoException( "Kernel options are too long, 2048 chars exceeded: %s" % k_args) utils.subprocess_call([ 'kexec', '--load', '--initrd=%s' % (self.safe_load(profile_data, 'initrd_local'),), '--command-line=%s' % (k_args,), self.safe_load(profile_data, 'kernel_local') ]) print("Kernel loaded; run 'kexec -e' to execute") return self.net_install(after_download) def get_boot_loader_info(self): cmd = ["/sbin/grubby", "--bootloader-probe"] probe_process = subprocess.Popen(cmd, stdout=subprocess.PIPE) which_loader = probe_process.communicate()[0].decode() return probe_process.returncode, which_loader def replace(self): """ Handle morphing an existing system through downloading new kernel, new initrd, and installing a autoinst in the initrd, then manipulating grub. """ try: shutil.rmtree("/var/spool/koan") except OSError as xxx_todo_changeme: (err, msg) = xxx_todo_changeme.args if err != errno.ENOENT: raise try: os.makedirs("/var/spool/koan") except OSError as xxx_todo_changeme1: (err, msg) = xxx_todo_changeme1.args if err != errno.EEXIST: raise def after_download(self, profile_data): use_grubby = False use_grub2 = False (make, version) = utils.os_release() if make in ['ubuntu', 'debian']: if not os.path.exists("/usr/sbin/update-grub"): raise InfoException("grub2 is not installed") use_grub2 = True elif make == "suse": if not os.path.exists("/usr/sbin/grub2-install"): raise InfoException("grub2 is not installed") use_grub2 = True else: if not os.path.exists("/sbin/grubby"): raise InfoException("grubby is not installed") use_grubby = True k_args = self.calc_kernel_args(profile_data, replace_self=1) autoinst = self.safe_load(profile_data, 'autoinst') if (make == "centos" and version < 7) or (make == "redhat" and version < 7) or ( make == "fedora" and version < 10) or (make == "suse"): # embed the initrd in the autoinst file because of libdhcp and/or pump # needing the help due to some DHCP timeout potential in some certain # network configs. if self.embed_autoinst: self.build_initrd( self.safe_load(profile_data, 'initrd_local'), autoinst, profile_data ) arch_cmd = subprocess.Popen( "/bin/uname -m", stdout=subprocess.PIPE, shell=True) arch = arch_cmd.communicate()[0].decode() # Validate kernel argument length (limit depends on architecture -- # see asm-*/setup.h). For example: # asm-i386/setup.h:#define COMMAND_LINE_SIZE 256 # asm-powerpc/setup.h:#define COMMAND_LINE_SIZE 512 # asm-x86_64/setup.h:#define COMMAND_LINE_SIZE 256 # arch/x86/include/asm/setup.h:#define COMMAND_LINE_SIZE 2048 if arch.startswith("ppc"): if len(k_args) > 511: raise InfoException( "Kernel options are too long, 512 chars exceeded: %s" % k_args) elif len(k_args) > 2048: raise InfoException( "Kernel options are too long, 2048 chars exceeded: %s" % k_args) if use_grubby: cmd = [ "/sbin/grubby", "--add-kernel", self.safe_load( profile_data, 'kernel_local'), "--initrd", self.safe_load( profile_data, 'initrd_local'), "--args", "%s" % k_args] if not self.no_copy_default: cmd.append("--copy-default") boot_probe_ret_code, probe_output = self.get_boot_loader_info() if boot_probe_ret_code == 0 and probe_output.find("lilo") >= 0: cmd.append("--lilo") if self.add_reinstall_entry: cmd.append("--title=Reinstall") else: cmd.append("--make-default") cmd.append("--title=kick%s" % int(time.time())) if self.live_cd: cmd.append("--bad-image-okay") cmd.append("--boot-filesystem=/") cmd.append("--config-file=/tmp/boot/boot/grub/grub.conf") # Are we running on ppc? if arch.startswith("ppc"): if "grub2" in probe_output: cmd.append("--grub2") else: cmd.append("--yaboot") utils.subprocess_call(cmd) # Need to remove the root= argument to prevent booting the current OS cmd = [ "/sbin/grubby", "--update-kernel", self.safe_load( profile_data, 'kernel_local'), "--remove-args=root"] utils.subprocess_call(cmd) # Any post-grubby processing required (e.g. ybin, zipl, lilo)? if arch.startswith("ppc") and "grub2" not in probe_output: # FIXME - CHRP hardware uses a 'PPC PReP Boot' partition # and doesn't require running ybin print("- applying ybin changes") cmd = ["/sbin/ybin"] utils.subprocess_call(cmd) else: # if grubby --bootloader-probe returns lilo, # apply lilo changes if boot_probe_ret_code == 0 and probe_output.find("lilo") != -1: print("- applying lilo changes") cmd = ["/sbin/lilo"] utils.subprocess_call(cmd) elif use_grub2: # Use grub2 for --replace-self kernel_local = utils.get_grub_real_path(self.safe_load(profile_data, 'kernel_local')) initrd_local = utils.get_grub_real_path(self.safe_load(profile_data, 'initrd_local')) # Set name for grub2 menuentry if self.add_reinstall_entry: name = "Reinstall: %s" % profile_data['name'] else: name = "%s" % profile_data['name'] # Set paths for Ubuntu/Debian # TODO: Add support for other distros when they ship grub2 if make in ['ubuntu', 'debian', 'suse']: grub_file = "/etc/grub.d/42_koan" grub_default_file = "/etc/default/grub" if make in ['suse']: cmd = ['/sbin/update-bootloader', '--refresh'] else: cmd = ["update-grub"] default_cmd = [ 'sed', '-i', 's/^GRUB_DEFAULT\\=.*$/GRUB_DEFAULT="%s"/g' % name, grub_default_file] # Create grub2 menuentry linux_cmd = "linux" initrd_cmd = "initrd" if utils.is_uefi_system(): linux_cmd = "linuxefi" initrd_cmd = "initrdefi" grub_entry = """ cat <<EOF menuentry "{name}" {{ {linux_cmd} {kernel} {args} {initrd_cmd} {initrd} }} EOF """.format(name=name, kernel=kernel_local, args=k_args, initrd=initrd_local, linux_cmd=linux_cmd, initrd_cmd=initrd_cmd) # Save grub2 menuentry with open(grub_file, "w") as grub_config_file: grub_config_file.write(grub_entry) os.chmod(grub_file, 0o755) # Set default grub entry for reboot if not self.add_reinstall_entry: print("- setting grub2 default entry") subprocess.call(default_cmd) # Run update-grub utils.subprocess_call(cmd) if not self.add_reinstall_entry: print("- reboot to apply changes") else: print("- reinstallation entry added") return self.net_install(after_download) def get_insert_script(self, initrd): """ Create bash script for inserting autoinst into initrd. Code heavily borrowed from internal auto-ks scripts. """ return r""" cd /var/spool/koan mkdir initrd gzip -dc %s > initrd.tmp || xz -dc %s > initrd.tmp if mount -o loop -t ext2 initrd.tmp initrd >&/dev/null ; then cp ks.cfg initrd/ ln initrd/ks.cfg initrd/tmp/ks.cfg umount initrd gzip -c initrd.tmp > initrd_final else echo "mount failed; treating initrd as a cpio archive..." cd initrd cpio -id <../initrd.tmp cp /var/spool/koan/ks.cfg . ln ks.cfg tmp/ks.cfg find . | cpio -o -H newc | gzip -9 > ../initrd_final echo "...done" fi """ % (initrd, initrd) def build_initrd(self, initrd, autoinst, data): """ Crack open an initrd and install the autoinst file. """ # save autoinst to file ksdata = utils.urlread(autoinst) fd = open("/var/spool/koan/ks.cfg", "w+") if ksdata is not None: fd.write(ksdata) fd.close() # handle insertion of autoinst based on type of initrd fd = open("/var/spool/koan/insert.sh", "w+") fd.write(self.get_insert_script(initrd)) fd.close() utils.subprocess_call(["/bin/bash", "/var/spool/koan/insert.sh"]) shutil.copyfile("/var/spool/koan/initrd_final", initrd) def connect_fail(self): raise InfoException( "Could not communicate with %s:%s" % (self.server, self.port)) def get_data(self, what, name=None): try: if what[-1] == "s": data = getattr(self.xmlrpc_server, "get_%s" % what)() else: data = getattr( self.xmlrpc_server, "get_%s_as_rendered" % what)(name) except: traceback.print_exc() self.connect_fail() if data == {}: raise InfoException("No entry/entries found") return data def get_ips(self, strdata): """ Return a list of IP address strings found in argument. warning: not IPv6 friendly """ return re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', strdata) def get_macs(self, strdata): """ Return a list of MAC address strings found in argument. """ return re.findall( r'[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F:0-9]{2}:[A-F:0-9]{2}', strdata.upper()) def is_ip(self, strdata): """ Is strdata an IP? warning: not IPv6 friendly """ return self.get_ips(strdata) and True or False def is_mac(self, strdata): """ Return whether the argument is a mac address. """ return self.get_macs(strdata) and True or False def get_distro_files(self, profile_data, download_root): """ Using distro data (fetched from bootconf tree), determine what kernel and initrd to download, and save them locally. """ os.chdir(download_root) distro = self.safe_load(profile_data, 'distro') kernel = self.safe_load(profile_data, 'kernel') initrd = self.safe_load(profile_data, 'initrd') kernel_short = os.path.basename(kernel) initrd_short = os.path.basename(initrd) kernel_save = "%s/%s_koan" % (download_root, kernel_short) initrd_save = "%s/%s_koan" % (download_root, initrd_short) if self.server: if kernel[0] == "/": kernel = "http://%s/cobbler/images/%s/%s" % ( profile_data["http_server"], distro, kernel_short) if initrd[0] == "/": initrd = "http://%s/cobbler/images/%s/%s" % ( profile_data["http_server"], distro, initrd_short) try: print("downloading initrd %s to %s" % (initrd_short, initrd_save)) print("url=%s" % initrd) utils.urlgrab(initrd, initrd_save) print("downloading kernel %s to %s" % (kernel_short, kernel_save)) print("url=%s" % kernel) utils.urlgrab(kernel, kernel_save) except: traceback.print_exc() raise InfoException("error downloading files") profile_data['kernel_local'] = kernel_save profile_data['initrd_local'] = initrd_save def calc_kernel_args(self, pd, replace_self=0): autoinst = self.safe_load(pd, 'autoinst') options = self.safe_load(pd, 'kernel_options', default='') breed = self.safe_load(pd, 'breed') os_version = self.safe_load(pd, 'os_version') kextra = "" if autoinst is not None and autoinst != "": if breed is not None and breed == "suse": kextra = "autoyast=" + autoinst elif breed is not None and breed == "debian" or breed == "ubuntu": kextra = "auto-install/enable=true priority=critical url=" + \ autoinst else: kextra = "ks=" + autoinst if options != "": kextra = kextra + " " + options # parser issues? lang needs a trailing = and somehow doesn't have it. # convert the from-cobbler options back to a hash # so that we can override it in a way that works as intended hashv = utils.input_string_or_dict(kextra) if self.static_interface is not None and ( breed == "redhat" or breed == "suse" or breed == "debian" or breed == "ubuntu"): interface_name = self.static_interface interfaces = self.safe_load(pd, "interfaces") if interface_name.startswith("eth"): alt_interface_name = interface_name.replace("eth", "intf") interface_data = self.safe_load( interfaces, interface_name, alt_interface_name) else: interface_data = self.safe_load(interfaces, interface_name) ip = self.safe_load(interface_data, "ip_address") netmask = self.safe_load(interface_data, "netmask") gateway = self.safe_load(pd, "gateway") dns = self.safe_load(pd, "name_servers") hostname = self.safe_load(interface_data, "dns_name") if breed == "debian" or breed == "ubuntu": hostname = self.safe_load(pd, "hostname") name = self.safe_load(pd, "name") if hostname != "" or name != "": if hostname != "": # if this is a FQDN, grab the first bit my_hostname = hostname.split(".")[0] _domain = hostname.split(".")[1:] if _domain: my_domain = ".".join(_domain) else: my_hostname = name.split(".")[0] _domain = name.split(".")[1:] if _domain: my_domain = ".".join(_domain) hashv["hostname"] = my_hostname hashv["domain"] = my_domain if breed == "suse": hashv["netdevice"] = self.static_interface else: hashv["ksdevice"] = self.static_interface newdracut = False if (breed == "redhat" and ((os_version[0:4] == "rhel" and int(os_version[4:]) >= 7) or (os_version[0:6] == "fedora" and int(os_version[6:]) >= 17))): newdracut = True if ip is not None: if breed == "suse": hashv["hostip"] = ip elif breed == "debian" or breed == "ubuntu": hashv["netcfg/get_ipaddress"] = ip elif newdracut: def get_cidr(netmask): binary_str = '' for octet in netmask.split('.'): binary_str += bin(int(octet))[2:].zfill(8) return str(len(binary_str.rstrip('0'))) hashv["ip"] = "%s::%s:%s:%s:%s:none" % (ip, gateway, get_cidr(netmask), hostname, interface_name) else: hashv["ip"] = ip if netmask is not None: if breed == "debian" or breed == "ubuntu": hashv["netcfg/get_netmask"] = netmask elif newdracut: pass else: hashv["netmask"] = netmask if gateway is not None: if breed == "debian" or breed == "ubuntu": hashv["netcfg/get_gateway"] = gateway elif newdracut: pass else: hashv["gateway"] = gateway if dns is not None: if newdracut: if os_version[0:4] == "rhel" and int(os_version[4]) == 7: hashv["nameserver"] = dns[0] else: hashv["ip"] += ":" + ":".join(dns[0:2]) elif breed == "suse": hashv["nameserver"] = dns[0] elif breed == "debian" or breed == "ubuntu": hashv["netcfg/get_nameservers"] = " ".join(dns) else: hashv["dns"] = ",".join(dns) if replace_self and self.embed_autoinst: hashv["ks"] = "file:ks.cfg" if self.kopts_override is not None: hash2 = utils.input_string_or_dict(self.kopts_override) hashv.update(hash2) options = utils.dict_to_string(hashv) options = options.replace("lang ", "lang= ") # if using ksdevice=bootif that only works for PXE so replace # it with something that will work options = options.replace("ksdevice=bootif", "ksdevice=link") return options def virt_net_install(self, profile_data): """ Invoke virt guest-install (or tweaked copy thereof) """ pd = profile_data self.load_virt_modules() arch = self.safe_load(pd, 'arch', 'x86') kextra = self.calc_kernel_args(pd) (uuid, create_func, fullvirt, can_poll) = self.virt_choose(pd) virtname = self.calc_virt_name(pd) ram = self.calc_virt_ram(pd) vcpus = self.calc_virt_cpus(pd) path_list = self.calc_virt_path(pd, virtname) size_list = self.calc_virt_filesize(pd) driver_list = self.calc_virt_drivers(pd) if self.virt_type == 'openvz': disks = None else: disks = self.merge_disk_data(path_list, size_list, driver_list) virt_auto_boot = self.calc_virt_autoboot(pd, self.virt_auto_boot) virt_pxe_boot = self.calc_virt_pxeboot(pd, self.virt_pxe_boot) results = create_func( name=virtname, ram=ram, disks=disks, uuid=uuid, extra=kextra, vcpus=vcpus, profile_data=profile_data, arch=arch, gfx_type=self.gfx_type, fullvirt=fullvirt, bridge=self.virt_bridge, virt_type=self.virt_type, virt_auto_boot=virt_auto_boot, virt_pxe_boot=virt_pxe_boot, qemu_driver_type=self.qemu_disk_type, qemu_net_type=self.qemu_net_type, qemu_machine_type=self.qemu_machine_type, wait=self.virtinstall_wait, noreboot=self.virtinstall_noreboot, osimport=self.virtinstall_osimport, ) # print results if can_poll is not None and self.should_poll: import libvirt print("- polling for virt completion") conn = None if can_poll == "xen": conn = libvirt.open(None) elif can_poll == "qemu": conn = libvirt.open("qemu:///system") else: raise InfoException("Don't know how to poll this virt-type") ct = 0 while True: time.sleep(3) state = utils.get_vm_state(conn, virtname) if state == "running": print("- install is still running, sleeping for 1 minute " "(%s)" % ct) ct = ct + 1 time.sleep(60) elif state == "crashed": print("- the install seems to have crashed.") return "failed" elif state == "shutdown": print("- shutdown VM detected, is the install done? " "Restarting!") utils.find_vm(conn, virtname).create() return results else: raise InfoException("internal error, bad virt state") if virt_auto_boot: if self.virt_type in ["xenpv", "xenfv"]: if not utils.create_xendomains_symlink(virtname): print("- warning: failed to setup autoboot for %s, " "it will have to be configured manually" % virtname) elif self.virt_type in ["qemu", "kvm"]: utils.libvirt_enable_autostart(virtname) elif self.virt_type in ["openvz"]: pass else: print("- warning: don't know how to autoboot this virt type yet") # else... return results def load_virt_modules(self): try: from . import xencreate from . import qcreate from . import imagecreate assert xencreate assert qcreate assert imagecreate except: traceback.print_exc() raise InfoException( "no virtualization support available,\ install python-virtinst or virt-install?") def virt_choose(self, pd): fullvirt = False can_poll = None if (self.image is not None) and (pd["image_type"] == "virt-clone"): fullvirt = True uuid = None from . import imagecreate creator = imagecreate.start_install elif self.virt_type in ["xenpv", "xenfv"]: uuid = self.get_uuid(self.calc_virt_uuid(pd)) from . import xencreate creator = xencreate.start_install if self.virt_type == "xenfv": fullvirt = True can_poll = "xen" elif self.virt_type in ["qemu", "kvm"]: fullvirt = True uuid = None from . import qcreate creator = qcreate.start_install can_poll = "qemu" elif self.virt_type == "vmware": from . import vmwcreate uuid = None creator = vmwcreate.start_install elif self.virt_type == "vmwarew": import vmwwcreate uuid = None creator = vmwwcreate.start_install elif self.virt_type == "openvz": from . import openvzcreate uuid = None creator = openvzcreate.start_install else: raise InfoException("Unspecified virt type: %s" % self.virt_type) return (uuid, creator, fullvirt, can_poll) def merge_disk_data(self, paths, sizes, drivers): counter = 0 disks = [] for p in paths: path = paths[counter] if counter >= len(sizes): size = sizes[-1] else: size = sizes[counter] if counter >= len(drivers): driver = drivers[-1] else: driver = drivers[counter] disks.append([path, size, driver]) counter = counter + 1 if len(disks) == 0: print("paths: ", paths) print("sizes: ", sizes) print("drivers: ", drivers) raise InfoException("Disk configuration not resolvable!") return disks def calc_virt_name(self, profile_data): if self.virt_name is not None: # explicit override name = self.virt_name elif "interfaces" in profile_data: # this is a system object, just use the name name = profile_data["name"] else: # just use the time, we used to use the MAC # but that's not really reliable when there are more # than one. name = time.ctime(time.time()) # keep libvirt happy with the names return name.replace(":", "_").replace(" ", "_") def calc_virt_autoboot(self, data, override_autoboot=False): if override_autoboot: return True autoboot = self.safe_load(data, 'virt_auto_boot', 0) autoboot = str(autoboot).lower() if autoboot in ["1", "true", "y", "yes"]: return True return False def calc_virt_pxeboot(self, data, override_pxeboot=False): if override_pxeboot: return True pxeboot = self.safe_load(data, 'virt_pxe_boot', 0) pxeboot = str(pxeboot).lower() if pxeboot in ["1", "true", "y", "yes"]: return True return False def calc_virt_filesize(self, data, default_filesize=0): # MAJOR FIXME: are there overrides? size = self.safe_load(data, 'virt_file_size', 'xen_file_size', 0) tokens = str(size).split(",") accum = [] for t in tokens: accum.append(self.calc_virt_filesize2(data, size=t)) return accum def calc_virt_filesize2(self, data, default_filesize=1, size=0): """ Assign a virt filesize if none is given in the profile. """ err = False try: int(size) except: err = True if size is None or size == '': err = True if err: print("invalid file size specified, using defaults") return default_filesize return int(size) def calc_virt_drivers(self, data): driver = self.safe_load(data, 'virt_disk_driver', default='raw') tokens = driver.split(",") accum = [] for t in tokens: # FIXME: this list should be pulled out of # the virtinst VirtualDisk class, but # not all versions of virtinst have a # nice list to use if t in ('raw', 'qcow', 'qcow2', 'aio', 'vmdk', 'qed'): accum.append(t) else: print("invalid disk driver specified, defaulting to 'raw'") accum.append('raw') return accum def calc_virt_ram(self, data, default_ram=64): """ Assign a virt ram size if none is given in the profile. """ size = self.safe_load(data, 'virt_ram', 'xen_ram', 0) err = False try: int(size) except: err = True if size is None or size == '' or int(size) < default_ram: err = True if err: print("invalid RAM size specified, using defaults.") return default_ram return int(size) def calc_virt_cpus(self, data, default_cpus=1): """ Assign virtual CPUs if none is given in the profile. """ size = self.safe_load(data, 'virt_cpus', default=default_cpus) try: isize = int(size) except: traceback.print_exc() return default_cpus return isize def calc_virt_mac(self, data): if not self.is_virt: return None # irrelevant if self.is_mac(self.system): return self.system.upper() return utils.random_mac() def calc_virt_uuid(self, data): # TODO: eventually we may want to allow some koan CLI # option (or cobbler system option) for passing in the UUID. # Until then, it's random. return None """ Assign a UUID if none/invalid is given in the profile. """ my_id = self.safe_load(data, 'virt_uuid', 'xen_uuid', 0) uuid_re = re.compile( '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') err = False try: str(my_id) except: err = True if my_id is None or my_id == '' or not uuid_re.match(id): err = True if err and my_id is not None: print("invalid UUID specified. randomizing...") return None return my_id def calc_virt_path(self, pd, name): # input is either a single item or a string list # it's not in the arguments to this function .. it's from one of many # potential sources location = self.virt_path if location is None: # no explicit CLI override, what did the cobbler server say? location = self.safe_load(pd, 'virt_path', default=None) if location is None or location == "": # not set in cobbler either? then assume reasonable defaults if self.virt_type in ["xenpv", "xenfv"]: prefix = "/var/lib/xen/images/" elif self.virt_type in ["qemu", "kvm"]: prefix = "/var/lib/libvirt/images/" elif self.virt_type == "vmwarew": prefix = "/var/lib/vmware/%s/" % name else: prefix = "/var/lib/vmware/images/" if not os.path.exists(prefix): print("- creating: %s" % prefix) os.makedirs(prefix) return ["%s/%s-disk0" % (prefix, name)] # ok, so now we have a user that either through cobbler or some other # source *did* specify a location. It might be a list. virt_sizes = self.calc_virt_filesize(pd) path_splitted = location.split(",") paths = [] count = -1 for x in path_splitted: count = count + 1 path = self.calc_virt_path2( pd, name, offset=count, location=x, sizes=virt_sizes) paths.append(path) return paths def calc_virt_path2(self, pd, name, offset=0, location=None, sizes=[]): # Parse the command line to determine if this is a # path, a partition, or a volume group parameter # file Ex: /foo # partition Ex: /dev/foo # volume-group Ex: vg-name(:lv-name) # # chosing the disk image name (if applicable) is somewhat # complicated ... # use default location for the virt type if not location.startswith("/dev/") and location.startswith("/"): # filesystem path if os.path.isdir(location): return "%s/%s-disk%s" % (location, name, offset) elif not os.path.exists(location) \ and os.path.isdir(os.path.dirname(location)): return location else: if self.force_path: return location else: raise InfoException( "The location %s is an existing file. Consider '--force-path' to overwrite it." % location) elif location.startswith("/dev/"): # partition if os.path.exists(location): return location else: raise InfoException("virt path is not a valid block device") else: # it's a volume group, verify that it exists if location.find(':') == -1: vgname = location lvname = "%s-disk%s" % (name, offset) else: vgname, lvname = location.split(':')[:2] args = "vgs -o vg_name" print("%s" % args) vgnames = subprocess.Popen( args, shell=True, stdout=subprocess.PIPE).communicate()[0].decode() print(vgnames) if vgnames.find(vgname) == -1: raise InfoException( "The volume group [%s] does not exist." % vgname) # check free space args = "LANG=C vgs --noheadings -o vg_free --units g %s" % vgname print(args) cmd = subprocess.Popen(args, stdout=subprocess.PIPE, shell=True) freespace_str = cmd.communicate()[0].decode() freespace_str = freespace_str.split("\n")[0].strip() freespace_str = freespace_str.lower().replace( "g", "").replace( ',', '.') # remove gigabytes print("(%s)" % freespace_str) freespace = int(float(freespace_str)) virt_size = self.calc_virt_filesize(pd) if len(virt_size) > offset: virt_size = sizes[offset] else: return sizes[-1] if freespace >= int(virt_size): # look for LVM partition named foo, create if doesn't exist args = "lvs --noheadings -o lv_name %s" % vgname print("%s" % args) lvs_str = subprocess.Popen( args, stdout=subprocess.PIPE, shell=True).communicate()[0].decode() print(lvs_str) # have to create it? found_lvs = False for lvs in lvs_str.split("\n"): if lvs.strip() == lvname: found_lvs = True break if not found_lvs: args = "lvcreate -L %sG -n %s %s" % ( virt_size, lvname, vgname) print("%s" % args) lv_create = subprocess.call(args, shell=True) if lv_create != 0: raise InfoException("LVM creation failed") # partition location partition_location = "/dev/mapper/%s-%s" % ( vgname.replace('-', '--'), lvname.replace('-', '--')) # check whether we have SELinux enabled system args = "/usr/sbin/selinuxenabled" if os.path.exists(args) and subprocess.call(args) == 0: # required context type context_type = "virt_image_t" # change security context type to required one args = "/usr/bin/chcon -t %s %s" % ( context_type, partition_location) print("%s" % args) change_context = subprocess.call( args, close_fds=True, shell=True) # modify SELinux policy in order to preserve security context # between reboots args = "/usr/sbin/semanage fcontext -a -t %s %s" % ( context_type, partition_location) print("%s" % args) change_context |= subprocess.call( args, close_fds=True, shell=True) if change_context != 0: raise InfoException( "SELinux security context setting to LVM partition failed") # return partition location return partition_location else: raise InfoException( "volume group needs %s GB free space." % virt_size) def randomUUID(self): """ Generate a random UUID. Copied from xend/uuid.py """ rc = [] for x in range(0, 16): rc.append(random.randint(0, 255)) return rc def uuidToString(self, u): """ return uuid as a string """ return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2, "%02x" * 6]) % tuple(u) def get_uuid(self, uuid): """ return the passed-in uuid, or a random one if it's not set. """ if uuid: return uuid return self.uuidToString(self.randomUUID()) if __name__ == "__main__": main() 07070100000035000081A400000000000000000000000160FFEB8C00000639000000000000000000000000000000000000001F00000000koan-3.0.1/koan/cexceptions.py""" Custom exceptions for Cobbler Copyright 2006-2009, Red Hat, Inc and Others Michael DeHaan <michael.dehaan AT gmail> 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 """ class KoanException(Exception): def __init__(self, value, *args): if args: self.value = value % args else: self.value = value # this is a hack to work around some odd exception handling in older pythons self.from_koan = 1 def __str__(self): return repr(self.value) class KX(KoanException): pass class FileNotFoundException(KoanException): pass class InfoException(Exception): """ Custom exception for tracking of fatal errors. """ def __init__(self, value, **args): self.value = value % args self.from_koan = 1 def __str__(self): return repr(self.value) class VirtCreateException(Exception): pass class OVZCreateException(Exception): pass 07070100000036000081A400000000000000000000000160FFEB8C00002927000000000000000000000000000000000000002000000000koan-3.0.1/koan/configurator.py""" Configuration class. Copyright 2010 Kelsey Hightower Kelsey Hightower <kelsey.hightower@gmail.com> 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 module for configuring repos, packages and files """ import filecmp import shutil from . import utils import tempfile import stat import os.path import sys import time import pwd import grp import json try: import yum sys.path.append('/usr/share/yum-cli') import cli yum_available = True except: yum_available = False class KoanConfigure: """ Used for all configuration methods, used by koan to configure repos, files and packages. """ def __init__(self, config): """Constructor. Requires json config object.""" self.config = json.JSONDecoder().decode(config) self.stats = {} (self.dist, _) = utils.os_release() def configure_repos(self): # Enables the possibility to use different types of repos if yum_available and self.dist == "redhat": self.configure_yum_repos() def configure_yum_repos(self): """Configure YUM repositories.""" print("- Configuring Repos") old_repo = '/etc/yum.repos.d/config.repo' # Stage a tempfile to hold new file contents _tempfile = tempfile.NamedTemporaryFile() _tempfile.write(self.config['repo_data']) _tempfile.flush() new_repo = _tempfile.name # Check if repo resource exist, create if missing if os.path.isfile(old_repo): if not filecmp.cmp(old_repo, new_repo): utils.sync_file(old_repo, new_repo, 0, 0, 644) self.stats['repos_status'] = "Success: Repos in sync" else: self.stats['repos_status'] = "Success: Repos in sync" else: print(" %s not found, creating..." % old_repo) open(old_repo, 'w').close() utils.sync_file(old_repo, new_repo, 0, 0, 644) self.stats['repos_status'] = "Success: Repos in sync" _tempfile.close() def configure_packages(self): # Enables the possibility to use different types of package # configurators if yum_available and self.dist == "redhat": self.configure_yum_packages() def configure_yum_packages(self): """Configure package resources.""" print("- Configuring Packages") runtime_start = time.time() nsync = 0 osync = 0 fail = 0 packages = self.config['packages'] yb = yum.YumBase() yb.preconf.debuglevel = 0 yb.preconf.errorlevel = 0 yb.doTsSetup() yb.doRpmDBSetup() ybc = cli.YumBaseCli() ybc.preconf.debuglevel = 0 ybc.preconf.errorlevel = 0 ybc.conf.assumeyes = True ybc.doTsSetup() ybc.doRpmDBSetup() create_pkg_list = [] remove_pkg_list = [] for package in packages: action = packages[package]['action'] # In the near future, will use install_name vs package # as it includes a more specific package name: "package-version" # install_name = packages[package]['install_name'] if yb.isPackageInstalled(package): if action == 'create': nsync += 1 if action == 'remove': remove_pkg_list.append(package) if not yb.isPackageInstalled(package): if action == 'create': create_pkg_list.append(package) if action == 'remove': nsync += 1 # Don't waste time with YUM if there is nothing to do. doTransaction = False if create_pkg_list: print(" Packages out of sync: %s" % create_pkg_list) ybc.installPkgs(create_pkg_list) osync += len(create_pkg_list) doTransaction = True if remove_pkg_list: print(" Packages out of sync: %s" % remove_pkg_list) ybc.erasePkgs(remove_pkg_list) osync += len(remove_pkg_list) doTransaction = True if doTransaction: ybc.buildTransaction() ybc.doTransaction() runtime_end = time.time() runtime = (runtime_end - runtime_start) self.stats['pkg'] = { 'runtime': runtime, 'nsync': nsync, 'osync': osync, 'fail': fail} def configure_directories(self): """ Configure directory resources.""" print("- Configuring Directories") runtime_start = time.time() nsync = 0 osync = 0 fail = 0 files = self.config['files'] # Split out directories _dirs = [d for d in files if files[d]['is_dir']] # Configure directories first for dir in _dirs: action = files[dir]['action'] odir = files[dir]['path'] protected_dirs = [ '/', '/bin', '/boot', '/dev', '/etc', '/lib', '/lib64', '/proc', '/sbin', '/sys', '/usr', '/var'] if os.path.isdir(odir): if os.path.realpath(odir) in protected_dirs: print(" %s is a protected directory, skipping..." % os.path.realpath(odir)) fail += 1 continue if action == 'create': nmode = int(files[dir]['mode'], 8) nuid = pwd.getpwnam(files[dir]['owner'])[2] ngid = grp.getgrnam(files[dir]['group'])[2] # Compare old and new directories, sync if permissions mismatch if os.path.isdir(odir): dstat = os.stat(odir) omode = stat.S_IMODE(dstat.st_mode) ouid = pwd.getpwuid(dstat.st_uid)[2] ogid = grp.getgrgid(dstat.st_gid)[2] if omode != nmode or ouid != nuid or ogid != ngid: os.chmod(odir, nmode) os.chown(odir, nuid, ngid) osync += 1 else: nsync += 1 else: print(" Directory out of sync, creating %s" % odir) os.makedirs(odir, nmode) os.chown(odir, nuid, ngid) osync += 1 elif action == 'remove': if os.path.isdir(odir): print(" Directory out of sync, removing %s" % odir) shutil.rmtree(odir) osync += 1 else: nsync += 1 else: pass runtime_end = time.time() runtime = (runtime_end - runtime_start) self.stats['dir'] = { 'runtime': runtime, 'nsync': nsync, 'osync': osync, 'fail': fail} def configure_files(self): """ Configure file resources.""" print("- Configuring Files") runtime_start = time.time() nsync = 0 osync = 0 fail = 0 files = self.config['files'] # Split out files _files = [f for f in files if files[f]['is_dir'] is False] for file in _files: action = files[file]['action'] ofile = files[file]['path'] if action == 'create': nmode = int(files[file]['mode'], 8) nuid = pwd.getpwnam(files[file]['owner'])[2] ngid = grp.getgrnam(files[file]['group'])[2] # Stage a tempfile to hold new file contents _tempfile = tempfile.NamedTemporaryFile() _tempfile.write(files[file]['content']) _tempfile.flush() nfile = _tempfile.name # Compare new and old files, sync if permissions or contents # mismatch if os.path.isfile(ofile): fstat = os.stat(ofile) omode = stat.S_IMODE(fstat.st_mode) ouid = pwd.getpwuid(fstat.st_uid)[2] ogid = grp.getgrgid(fstat.st_gid)[2] if not filecmp.cmp(ofile, nfile) or omode != nmode or ogid != ngid or ouid != nuid: utils.sync_file(ofile, nfile, nuid, ngid, nmode) osync += 1 else: nsync += 1 elif os.path.dirname(ofile): # Create the file only if the base directory exists open(ofile, 'w').close() utils.sync_file(ofile, nfile, nuid, ngid, nmode) osync += 1 else: print(" Base directory not found, %s required." % (os.path.dirname(ofile))) fail += 1 _tempfile.close() elif action == 'remove': if os.path.isfile(file): os.remove(ofile) osync += 1 else: nsync += 1 else: pass runtime_end = time.time() runtime = (runtime_end - runtime_start) self.stats['files'] = { 'runtime': runtime, 'nsync': nsync, 'osync': osync, 'fail': fail} def run(self): # Configure resources in a specific order: repos, packages, # directories, files if self.config['repos_enabled']: self.configure_repos() self.configure_packages() self.configure_directories() self.configure_files() return self.stats 07070100000037000081A400000000000000000000000160FFEB8C000004F5000000000000000000000000000000000000001F00000000koan-3.0.1/koan/imagecreate.py""" Virtualization installation functions for image based deployment Copyright 2008 Red Hat, Inc and Others. Bryan Kearney <bkearney@redhat.com> Original version based on virt-image David Lutterkort <dlutter@redhat.com> 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 """ from . import utils from . import virtinstall def start_install(*args, **kwargs): cmd = virtinstall.build_commandline("import", *args, **kwargs) rc, result, result_stderr = utils.subprocess_get_response(cmd, ignore_rc=True, get_stderr=True) if rc != 0: raise utils.InfoException("command failed (%s): %s %s" % (rc, result, result_stderr)) 07070100000038000081A400000000000000000000000160FFEB8C000015B3000000000000000000000000000000000000002000000000koan-3.0.1/koan/openvzcreate.py""" OpenVZ container-type virtualization installation functions. Copyright 2012 Artem Kanarev <kanarev AT tncc.ru>, Sergey Podushkin <psv AT tncc.ru> 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 """ import os from .cexceptions import OVZCreateException def start_install(*args, **kwargs): # check for Openvz tools presence # can be this apps installed in some other place? vzcfgvalidate = '/usr/sbin/vzcfgvalidate' vzctl = '/usr/sbin/vzctl' if not os.path.exists(vzcfgvalidate) or not os.path.exists(vzctl): raise OVZCreateException( "Cannot find %s and/or %s! Are OpenVZ tools installed?" % (vzcfgvalidate, vzctl) ) # params, that can be defined/redefined through ks_meta keys_for_meta = [ 'KMEMSIZE', # "14372700:14790164", 'LOCKEDPAGES', # "2048:2048", 'PRIVVMPAGES', # "65536:69632", 'SHMPAGES', # "21504:21504", 'NUMPROC', # "240:240", 'VMGUARPAGES', # "33792:unlimited", 'OOMGUARPAGES', # "26112:unlimited", 'NUMTCPSOCK', # "360:360", 'NUMFLOCK', # "188:206", 'NUMPTY', # "16:16", 'NUMSIGINFO', # "256:256", 'TCPSNDBUF', # "1720320:2703360", 'TCPRCVBUF', # "1720320:2703360", 'OTHERSOCKBUF', # "1126080:2097152", 'DGRAMRCVBUF', # "262144:262144", 'NUMOTHERSOCK', # "120", 'DCACHESIZE', # "3409920:3624960", 'NUMFILE', # "9312:9312", 'AVNUMPROC', # "180:180", 'NUMIPTENT', # "128:128", 'DISKINODES', # "200000:220000", 'QUOTATIME', # "0", 'VE_ROOT', # "/vz/root/$VEID", 'VE_PRIVATE', # "/vz/private/$VEID", 'SWAPPAGES', # "0:1G", 'ONBOOT', # "yes" ] sysname = kwargs['name'] autoinst = kwargs['profile_data']['autoinst'] # we use it for --ostemplate parameter template = kwargs['profile_data']['breed'] hostname = kwargs['profile_data']['hostname'] ipadd = kwargs['profile_data']['ip_address_eth0'] nameserver = kwargs['profile_data']['name_servers'][0] diskspace = kwargs['profile_data']['virt_file_size'] physpages = kwargs['profile_data']['virt_ram'] cpus = kwargs['profile_data']['virt_cpus'] onboot = kwargs['profile_data']['virt_auto_boot'] # we get [0,1] ot [False,True] and have to map it to [no,yes] onboot = 'yes' if onboot == '1' or onboot else 'no' CTID = None vz_meta = {} # get all vz_ parameters from ks_meta for item in kwargs['profile_data']['ks_meta'].split(): var = item.split('=') if var[0].startswith('vz_'): vz_meta[var[0].replace('vz_', '').upper()] = var[1] if 'CTID' in vz_meta and vz_meta['CTID']: try: CTID = int(vz_meta['CTID']) del vz_meta['CTID'] except ValueError: print("Invalid CTID in ks_meta. Exiting...") return 1 else: raise OVZCreateException( 'Mandatory "vz_ctid" parameter not found in ks_meta!') confiname = '/etc/vz/conf/%d.conf' % CTID # this is the minimal config. we can define additional parameters or # override some of them in ks_meta min_config = { 'PHYSPAGES': "0:%sM" % physpages, 'SWAPPAGES': "0:1G", 'DISKSPACE': "%sG:%sG" % (diskspace, diskspace), 'DISKINODES': "200000:220000", 'QUOTATIME': "0", 'CPUUNITS': "1000", 'CPUS': cpus, 'VE_ROOT': "/vz/root/$VEID", 'VE_PRIVATE': "/vz/private/$VEID", 'OSTEMPLATE': template, 'NAME': sysname, 'HOSTNAME': hostname, 'IP_ADDRESS': ipadd, 'NAMESERVER': nameserver, } # merge with override full_config = dict( [ (k, vz_meta[k] if k in vz_meta and k in keys_for_meta else min_config[k]) for k in set(min_config.keys() + vz_meta.keys())] ) # write config file for container f = open(confiname, 'w+') for key, val in full_config.items(): f.write('%s="%s"\n' % (key, val)) f.close() # validate the config file cmd = '%s %s' % (vzcfgvalidate, confiname) if not os.system(cmd.strip()): # now install the container tree cmd = '/usr/bin/ovz-install %s %s %s' % ( sysname, autoinst, full_config['VE_PRIVATE'].replace('$VEID', '%d' % CTID) ) if not os.system(cmd.strip()): # if everything fine, start the container cmd = '%s start %s' % (vzctl, CTID) if os.system(cmd.strip()): raise OVZCreateException("Start container %s failed" % CTID) else: raise OVZCreateException("Container creation %s failed" % CTID) else: raise OVZCreateException( "Container %s config file is not valid" % CTID) 07070100000039000081ED00000000000000000000000160FFEB8C00000835000000000000000000000000000000000000001B00000000koan-3.0.1/koan/qcreate.py""" Virtualization installation functions. Copyright 2007-2008 Red Hat, Inc and Others. Michael DeHaan <michael.dehaan AT gmail> 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 module for creating fullvirt guests via KVM/kqemu/qemu requires python-virtinst-0.200 (or virt-install in later distros). """ from xml.dom.minidom import parseString from koan.cexceptions import InfoException from koan import utils from koan import virtinstall def start_install(*args, **kwargs): if 'arch' in kwargs.keys(): kwargs['arch'] = None # use host arch for kvm acceleration # Use kvm acceleration if available try: import libvirt except: raise InfoException("package libvirt is required for installing virtual guests") conn = libvirt.openReadOnly(None) # See http://libvirt.org/formatcaps.html capabilities = parseString(conn.getCapabilities()) for domain in capabilities.getElementsByTagName("domain"): attributes = dict(domain.attributes.items()) if 'type' in attributes.keys() and attributes['type'] == 'kvm': kwargs['virt_type'] = 'kvm' break virtinstall.create_image_file(*args, **kwargs) cmd = virtinstall.build_commandline("qemu:///system", *args, **kwargs) rc, result, result_stderr = utils.subprocess_get_response(cmd, ignore_rc=True, get_stderr=True) if rc != 0: raise InfoException("command failed (%s): %s %s" % (rc, result, result_stderr)) 0707010000003A000081ED00000000000000000000000160FFEB8C000014F0000000000000000000000000000000000000001C00000000koan-3.0.1/koan/register.py""" registration tool for cobbler. Copyright 2009 Red Hat, Inc and Others. Michael DeHaan <michael.dehaan AT gmail> 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 """ import os import traceback from optparse import OptionParser import time import sys import socket from . import utils from .cexceptions import InfoException # usage: cobbler-register [--server=server] [--fqdn=hostname] --profile=foo def main(): """ Command line stuff... """ p = OptionParser() p.add_option( "-s", "--server", dest="server", default=os.environ.get("COBBLER_SERVER", ""), help="attach to this cobbler server" ) p.add_option( "-f", "--fqdn", dest="hostname", default="", help="override the discovered hostname" ) p.add_option( "-p", "--port", dest="port", default="80", help="cobbler port (default 80)" ) p.add_option( "-P", "--profile", dest="profile", default="", help="assign this profile to this system" ) p.add_option( "-b", "--batch", dest="batch", action="store_true", help="indicates this is being run from a script" ) (options, args) = p.parse_args() # if not os.getuid() == 0: # print("koan requires root access") # return 3 try: k = Register() k.server = options.server k.port = options.port k.profile = options.profile k.hostname = options.hostname k.batch = options.batch k.run() except Exception as e: (xa, xb, tb) = sys.exc_info() try: getattr(e, "from_koan") print(str(e)[1:-1]) # nice exception, no traceback needed except: print(xa) print(xb) print("".join(traceback.format_list(traceback.extract_tb(tb)))) return 1 return 0 class Register: def __init__(self): """ Constructor. Arguments will be filled in by optparse... """ self.server = "" self.port = "" self.profile = "" self.hostname = "" self.batch = "" def run(self): """ Commence with the registration already. """ # not really required, but probably best that ordinary users don't try # to run this not knowing what it does. if os.getuid() != 0: raise InfoException("root access is required to register") print("- preparing to koan home") self.conn = utils.connect_to_server(self.server, self.port) reg_info = {} print("- gathering network info") netinfo = utils.get_network_info() reg_info["interfaces"] = netinfo print("- checking hostname") sysname = "" if self.hostname != "" and self.hostname != "*AUTO*": hostname = self.hostname sysname = self.hostname else: hostname = socket.getfqdn() if hostname == "localhost.localdomain": if self.hostname == '*AUTO*': hostname = "" sysname = str(time.time()) else: raise InfoException( "must specify --fqdn, could not discover") if sysname == "": sysname = hostname if self.profile == "": raise InfoException("must specify --profile") # we'll do a profile check here just to avoid some log noise on the remote end. # network duplication checks and profile checks also happen on the # remote end. avail_profiles = self.conn.get_profiles() matched_profile = False for x in avail_profiles: if x.get("name", "") == self.profile: matched_profile = True break reg_info['name'] = sysname reg_info['profile'] = self.profile reg_info['hostname'] = hostname if not matched_profile: raise InfoException( "no such remote profile, see 'koan --list-profiles'") if not self.batch: self.conn.register_new_system(reg_info) print("- registration successful, new system name: %s" % sysname) else: try: self.conn.register_new_system(reg_info) print("- registration successful, new system name: %s" % sysname) except: traceback.print_exc() print("- registration failed, ignoring because of --batch") return if __name__ == "__main__": main() 0707010000003B000081A400000000000000000000000160FFEB8C00004893000000000000000000000000000000000000001900000000koan-3.0.1/koan/utils.py""" koan = kickstart over a network general usage functions Copyright 2006-2008 Red Hat, Inc and Others. Michael DeHaan <michael.dehaan AT gmail> 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 """ import os import random import tempfile import traceback import distro import urllib.request import xmlrpc.client import netifaces import subprocess import shutil import sys import time from .cexceptions import InfoException VIRT_STATE_NAME_MAP = { 0: "running", 1: "running", 2: "running", 3: "paused", 4: "shutdown", 5: "shutdown", 6: "crashed" } VALID_DRIVER_TYPES = ['raw', 'qcow', 'qcow2', 'vmdk', 'qed'] def setupLogging(appname): """ set up logging ... code borrowed/adapted from virt-manager """ import logging.handlers dateFormat = "%a, %d %b %Y %H:%M:%S" fileFormat = "[%(asctime)s " + appname + \ " %(process)d] %(levelname)s (%(module)s:%(lineno)d) %(message)s" streamFormat = "%(asctime)s %(levelname)-8s %(message)s" filename = "/var/log/koan/koan.log" rootLogger = logging.getLogger() rootLogger.setLevel(logging.DEBUG) fileHandler = logging.handlers.RotatingFileHandler(filename, "a", 1024 * 1024, 5) fileHandler.setFormatter(logging.Formatter(fileFormat, dateFormat)) rootLogger.addHandler(fileHandler) streamHandler = logging.StreamHandler(sys.stderr) streamHandler.setFormatter(logging.Formatter(streamFormat, dateFormat)) streamHandler.setLevel(logging.DEBUG) rootLogger.addHandler(streamHandler) def urlread(url): """ to support more distributions, implement (roughly) some parts of urlread and urlgrab from urlgrabber, in ways that are less cool and less efficient. """ print("- reading URL: %s" % url) if url is None or url == "": raise InfoException("invalid URL: %s" % url) elif url[0:3] == "nfs": try: ndir = os.path.dirname(url[6:]) nfile = os.path.basename(url[6:]) nfsdir = tempfile.mkdtemp(prefix="koan_nfs", dir="/tmp") nfsfile = os.path.join(nfsdir, nfile) cmd = ["mount", "-t", "nfs", "-o", "ro", ndir, nfsdir] subprocess_call(cmd) fd = open(nfsfile) data = fd.read() fd.close() cmd = ["umount", nfsdir] subprocess_call(cmd) return data except: traceback.print_exc() raise InfoException("Couldn't mount and read URL: %s" % url) elif url[0:4] == "http": try: fd = urllib.request.urlopen(url) data = fd.read() fd.close() return data except: traceback.print_exc() raise InfoException("Couldn't download: %s" % url) elif url[0:4] == "file": try: fd = open(url[5:]) data = fd.read() fd.close() return data except: raise InfoException("Couldn't read file from URL: %s" % url) else: raise InfoException("Unhandled URL protocol: %s" % url) def urlgrab(url, saveto): """ like urlread, but saves contents to disk. see comments for urlread as to why it's this way. """ data = urlread(url) fd = open(saveto, "w+b") fd.write(data) fd.close() def subprocess_call(cmd, ignore_rc=0): """ Wrapper around subprocess.call(...) """ print("- %s" % cmd) rc = subprocess.call(cmd) if rc != 0 and not ignore_rc: raise InfoException("command failed (%s)" % rc) return rc def subprocess_get_response(cmd, ignore_rc=False, get_stderr=False): """ Wrapper around subprocess.check_output(...) """ print("- %s" % cmd) rc = 0 result = "" if get_stderr: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: p = subprocess.Popen(cmd, stdout=subprocess.PIPE) result, stderr_result = p.communicate() rc = p.wait() if not ignore_rc and rc != 0: raise InfoException("command failed (%s)" % rc) if get_stderr: return rc, result.decode(), stderr_result.decode() return rc, result.decode() def input_string_or_dict(options, delim=None, allow_multiples=True): """ Older cobbler files stored configurations in a flat way, such that all values for strings. Newer versions of cobbler allow dictionaries. This function is used to allow loading of older value formats so new users of cobbler aren't broken in an upgrade. """ if options is None: return {} elif isinstance(options, list): raise InfoException("No idea what to do with list: %s" % options) elif isinstance(options, type("")): new_dict = {} tokens = options.split(delim) for t in tokens: tokens2 = t.split("=", 1) if len(tokens2) == 1: # this is a singleton option, no value key = tokens2[0] value = None else: key = tokens2[0] value = tokens2[1] # if we're allowing multiple values for the same key, # check to see if this token has already been # inserted into the dictionary of values already if key in new_dict.keys() and allow_multiples: # if so, check to see if there is already a list of values # otherwise convert the dictionary value to an array, and add # the new value to the end of the list if isinstance(new_dict[key], list): new_dict[key].append(value) else: new_dict[key] = [new_dict[key], value] else: new_dict[key] = value # dict.pop is not avail in 2.2 if "" in new_dict: del new_dict[""] return new_dict elif isinstance(options, type({})): options.pop('', None) return options else: raise InfoException("invalid input type: %s" % type(options)) def dict_to_string(hash): """ Convert a hash to a printable string. used primarily in the kernel options string and for some legacy stuff where koan expects strings (though this last part should be changed to hashes) """ buffer = "" if not isinstance(hash, dict): return hash for key in hash: value = hash[key] if value is None: buffer = buffer + str(key) + " " elif isinstance(value, list): # this value is an array, so we print out every # key=value for item in value: buffer = buffer + str(key) + "=" + str(item) + " " else: buffer = buffer + str(key) + "=" + str(value) + " " return buffer def nfsmount(input_path): # input: [user@]server:/foo/bar/x.img as string # output: (dirname where mounted, last part of filename) as 2-element tuple # we have to mount it first filename = input_path.split("/")[-1] dirpath = "/".join(input_path.split("/")[:-1]) tempdir = tempfile.mkdtemp(suffix='.mnt', prefix='koan_', dir='/tmp') mount_cmd = [ "/bin/mount", "-t", "nfs", "-o", "ro", dirpath, tempdir ] print("- running: %s" % mount_cmd) rc = subprocess.call(mount_cmd) if not rc == 0: shutil.rmtree(tempdir, ignore_errors=True) raise InfoException("nfs mount failed: %s" % dirpath) # NOTE: option for a blocking install might be nice, so we could do this # automatically, if supported by virt-install print("after install completes, you may unmount and delete %s" % tempdir) return (tempdir, filename) def get_vms(conn): """ Get virtual machines @param ? conn @return list virtual machines """ vms = [] # this block of code borrowed from virt-manager: # get working domain's name ids = conn.listDomainsID() for id in ids: vm = conn.lookupByID(id) vms.append(vm) # get defined domain names = conn.listDefinedDomains() for name in names: vm = conn.lookupByName(name) vms.append(vm) return vms def find_vm(conn, vmid): """ Extra bonus feature: vmid = -1 returns a list of everything This function from Func: fedorahosted.org/func """ vms = get_vms(conn) for vm in vms: if vm.name() == vmid: return vm raise InfoException("koan could not find the VM to watch: %s" % vmid) def get_vm_state(conn, vmid): """ Returns the state of a libvirt VM, by name. From Func: fedorahosted.org/func """ state = find_vm(conn, vmid).info()[0] return VIRT_STATE_NAME_MAP.get(state, "unknown") def os_release(): """ This code detects your os with the distro module and return the name and version. If it is not detected correctly it returns "unknown" (str) and "0" (float). :returns tuple (str, float) WHERE str is the name int is the version number """ distroname = distro.id() distrolike = distro.like() version = distro.version() redhat = ["centos", "fedora", "rhel"] if distroname in redhat or distrolike in redhat: if distroname in ["centos", "fedora"]: return distroname, float(version) else: return "redhat", float(version) if distroname in ["debian", "ubuntu"]: return distroname, float(version) if "suse" in distrolike: return "suse", float(version) return "unknown", 0.0 def uniqify(lst, purge=None): temp = {} for x in lst: temp[x] = 1 if purge is not None: temp2 = {} for x in temp.keys(): if x != purge: temp2[x] = 1 temp = temp2 return list(temp.keys()) def get_network_info(): interfaces = {} # get names inames = netifaces.interfaces() for iname in inames: mac = netifaces.ifaddresses(iname)[netifaces.AF_LINK][0]['addr'] if mac == "00:00:00:00:00:00": mac = "?" try: ip = netifaces.ifaddresses(iname)[netifaces.AF_INET][0]['addr'] if ip == "127.0.0.1": ip = "?" except: ip = "?" bridge = 0 module = "" try: nm = netifaces.ifaddresses(iname)[netifaces.AF_INET][0]['netmask'] except: nm = "?" interfaces[iname] = { "ip_address": ip, "mac_address": mac, "netmask": nm, "bridge": bridge, "module": module } # print interfaces return interfaces def connect_to_server(server=None, port=None): if server is None: server = os.environ.get("COBBLER_SERVER", "") if server == "": raise InfoException("--server must be specified") try_urls = [] if port is None: try_urls = [ "https://%s:443/cobbler_api" % (server), "http://%s:80/cobbler_api" % (server), ] else: try_urls = [ "https://%s:%s/cobbler_api" % (server, port), "http://%s:%s/cobbler_api" % (server, port), ] for url in try_urls: print("- looking for Cobbler at %s" % url) server = __try_connect(url) if server is not None: return server raise InfoException("Could not find Cobbler.") def create_xendomains_symlink(name): """ Create an /etc/xen/auto/<name> symlink for use with "xendomains"-style VM boot upon dom0 reboot. """ src = "/etc/xen/%s" % name dst = "/etc/xen/auto/%s" % name # Make sure symlink does not already exist. if os.path.exists(dst): print("Could not create %s symlink. File already exists in this " "location." % dst) return False # Verify that the destination is writable if not os.access(os.path.dirname(dst), os.W_OK): print("Could not create %s symlink. Please check write permissions " "and ownership." % dst) return False # check that xen config file exists and create symlink if os.path.exists(src): os.symlink(src, dst) return True else: print("Could not create %s symlink, source file %s is " "missing." % (dst, src)) return False def libvirt_enable_autostart(domain_name): import libvirt try: conn = libvirt.open("qemu:///system") conn.listDefinedDomains() domain = conn.lookupByName(domain_name) domain.setAutostart(1) except: raise InfoException("libvirt could not find domain %s" % domain_name) if not domain.autostart: raise InfoException( "Could not enable autostart on domain %s." % domain_name) def make_floppy(autoinst): (fd, floppy_path) = tempfile.mkstemp( suffix='.floppy', prefix='tmp', dir="/tmp") print("- creating floppy image at %s" % floppy_path) # create the floppy image file cmd = "dd if=/dev/zero of=%s bs=1440 count=1024" % floppy_path print("- %s" % cmd) rc = os.system(cmd) if not rc == 0: raise InfoException("dd failed") # vfatify cmd = "mkdosfs %s" % floppy_path print("- %s" % cmd) rc = os.system(cmd) if not rc == 0: raise InfoException("mkdosfs failed") # mount the floppy mount_path = tempfile.mkdtemp(suffix=".mnt", prefix='tmp', dir="/tmp") cmd = "mount -o loop -t vfat %s %s" % (floppy_path, mount_path) print("- %s" % cmd) rc = os.system(cmd) if not rc == 0: raise InfoException("mount failed") # download the autoinst file onto the mounted floppy print("- downloading %s" % autoinst) save_file = os.path.join(mount_path, "unattended.txt") urlgrab(autoinst, save_file) # umount cmd = "umount %s" % mount_path print("- %s" % cmd) rc = os.system(cmd) if not rc == 0: raise InfoException("umount failed") # return the path to the completed disk image to pass to virt-install return floppy_path def sync_file(ofile, nfile, uid, gid, mode): subprocess.call(['/usr/bin/diff', ofile, nfile]) shutil.copy(nfile, ofile) os.chmod(ofile, mode) os.chown(ofile, uid, gid) def __try_connect(url): try: xmlrpc_server = xmlrpc.client.ServerProxy(url) xmlrpc_server.ping() return xmlrpc_server except: traceback.print_exc() return None def create_qemu_image_file(path, size, driver_type): if driver_type not in VALID_DRIVER_TYPES: raise InfoException("Invalid QEMU image type: %s" % driver_type) cmd = ["qemu-img", "create", "-f", driver_type, path, "%sG" % size] try: subprocess_call(cmd) except: traceback.print_exc() raise InfoException( "Image file create failed: %s" % " ".join(cmd) ) def random_mac(): """ from xend/server/netif.py Generate a random MAC address. Uses OUI 00-50-56, allocated to VMWare. Last 3 fields are random. return: MAC address string """ mac = [0x00, 0x50, 0x56, random.randint(0x00, 0x3f), random.randint(0x00, 0xff), random.randint(0x00, 0xff)] return ':'.join(map(lambda x: "%02x" % x, mac)) def generate_timestamp(): return str(int(time.time())) def check_version_greater_or_equal(version1, version2): ass = version1.split(".") bss = version2.split(".") if len(ass) != len(bss): raise Exception("expected version format differs") for i, a in enumerate(ass): a = int(a) b = int(bss[i]) if a > b: return True if a < b: return False return True def is_uefi_system() -> bool: """ Helper function to check if the system we are currently running on is being booted by UEFI or a BIOS. :return: True if ``/sys/firmware/efi`` exists, otherwise False. """ if os.path.exists("/sys/firmware/efi"): return True return False def get_grub2_mkrelpath_executable() -> str: """ Searches through the path for the mkrelpath executable of GRUB2. :raises RuntimeError: In case the executable could not be found. :return: The path to the executable """ executable_path = "" binary_names = ["grub2-mkrelpath", "grub-mkrelpath"] for possible_name in binary_names: tmp_result = shutil.which(possible_name) if tmp_result is not None: executable_path = tmp_result break if not executable_path: raise RuntimeError("The executable for making a GRUB2 real path was not found. Tried executable names: \"%s\"" % str(binary_names)) return executable_path def get_grub_real_path(path: str): """ This function provides a wrapper to get the real path of a file to be able to write this to a grub config file. :param path: The path which should be converted. :raises FileNotFoundError: In case the path specifed did not exist. :raises RuntimeError: In case the executable did return a non-zero exitcode. :return: The value of ``path`` or the real path converted by ``grub2-mkrelpath``. """ if not os.path.exists(path): raise FileNotFoundError("Path specified did not exist on the filesystem.") command_result = subprocess.run( [get_grub2_mkrelpath_executable(), path], encoding=sys.getdefaultencoding(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) if command_result.returncode != 0: raise RuntimeError("Command executed did return non-zero exit code!") return command_result.stdout.strip() 0707010000003C000081A400000000000000000000000160FFEB8C00003D39000000000000000000000000000000000000001F00000000koan-3.0.1/koan/virtinstall.py""" Virtualization installation functions. Currently somewhat Xen/paravirt specific, will evolve later. Copyright 2006-2008 Red Hat, Inc. Michael DeHaan <mdehaan@redhat.com> Original version based on virtguest-install Jeremy Katz <katzj@redhat.com> Option handling added by Andrew Puch <apuch@redhat.com> Simplified for use as library by koan, Michael DeHaan <mdehaan@redhat.com> 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 """ import os import re import shlex from . import utils from .cexceptions import InfoException # The virtinst module will no longer be availabe to import in some # distros. We need to get all the info we need from the virt-install # command line tool. This should work on both old and new variants, # as the virt-install command line tool has always been provided by # python-virtinst (and now the new virt-install rpm). # virt-install 1.0.1 responds to --version on stderr. WTF? Check both. rc, response, stderr_response = utils.subprocess_get_response( shlex.split('virt-install --version'), True, True) if rc == 0: if response: virtinst_version = response else: virtinst_version = stderr_response else: virtinst_version = None # This one's trickier. We need a list of supported os varients, but # the man page explicitly says not to parse the result of this command. # But we need it, and there's no other way to get it. I spoke with the # virt-install maintainers and they said the point of that message # is that you can't absolutely depend on the output not changing, but # at the moment it's the only option for us. Long term plans are for # virt-install to switch to libosinfo for OS metadata tracking, which # provides a library and tools for querying valid OS values. Until # that's available and pervasive the best we can do is to use the # module if it's availabe and if not parse the command output. supported_variants = set() try: from virtinst import osdict for ostype in osdict.OS_TYPES.keys(): for variant in osdict.OS_TYPES[ostype]["variants"].keys(): supported_variants.add(variant) except: try: # This will fail on EL7+, gobble stderr to avoid confusing error # messages from being output rc, response, stderr_respose = utils.subprocess_get_response( shlex.split('virt-install --os-variant list'), False, True) variants = response.split('\n') for variant in variants: supported_variants.add(variant.split()[0]) except: try: # maybe on newer os using osinfo-query? rc, response = utils.subprocess_get_response( shlex.split('osinfo-query -f short-id os')) variants = response.split('\n') for variant in variants: supported_variants.add(variant.strip()) # osinfo-query does not list virtio26, add it here for fallback supported_variants.add('virtio26') except: # okay, probably on old os and we'll just use generic26 pass def _sanitize_disks(disks): ret = [] for d in disks: driver_type = None if len(d) > 2: driver_type = d[2] if d[1] != 0 or d[0].startswith("/dev"): ret.append((d[0], d[1], driver_type)) else: raise InfoException( "this virtualization type does not work without a disk image, set virt-size in Cobbler to non-zero" ) return ret def _sanitize_nics(nics, bridge, profile_bridge, network_count): ret = [] if network_count is not None and not nics: # Fill in some stub nics so we can take advantage of the loop logic nics = {} for i in range(int(network_count)): nics["foo%s" % i] = { "interface_type": "na", "mac_address": None, "virt_bridge": None, } if not nics: return ret interfaces = sorted(nics.keys()) counter = -1 vlanpattern = re.compile(r"[a-zA-Z0-9]+\.[0-9]+") for iname in interfaces: counter = counter + 1 intf = nics[iname] if (intf.get("interface_type", "") in ("bond", "bridge", "bonded_bridge_slave") or vlanpattern.match(iname) or iname.find(":") != -1): continue mac = intf["mac_address"] if not bridge: intf_bridge = intf["virt_bridge"] if intf_bridge == "": if profile_bridge == "": raise InfoException( "virt-bridge setting is not defined in cobbler") intf_bridge = profile_bridge else: if bridge.find(",") == -1: intf_bridge = bridge else: bridges = bridge.split(",") intf_bridge = bridges[counter] ret.append((intf_bridge, mac)) return ret def create_image_file(disks=None, **kwargs): disks = _sanitize_disks(disks) for path, size, driver_type in disks: if driver_type is None: continue if os.path.isdir(path) or os.path.exists(path): continue if str(size) == "0": continue utils.create_qemu_image_file(path, size, driver_type) def build_commandline(uri, name=None, ram=None, disks=None, uuid=None, extra=None, vcpus=None, profile_data=None, arch=None, gfx_type=None, fullvirt=False, bridge=None, virt_type=None, virt_auto_boot=False, virt_pxe_boot=False, qemu_driver_type=None, qemu_net_type=None, qemu_machine_type=None, wait=0, noreboot=False, osimport=False): # Set flags for CLI arguments based on the virtinst_version # tuple above. Older versions of python-virtinst don't have # a version easily accessible, so it will be None and we can # easily disable features based on that (RHEL5 and older usually) disable_autostart = False disable_virt_type = False disable_boot_opt = False disable_driver_type = False disable_net_model = False disable_machine_type = False oldstyle_macs = False oldstyle_accelerate = False if not virtinst_version: print("- warning: old virt-install detected, a lot of features will " "be disabled") disable_autostart = True disable_boot_opt = True disable_virt_type = True disable_driver_type = True disable_net_model = True disable_machine_type = True oldstyle_macs = True oldstyle_accelerate = True import_exists = False # avoid duplicating --import parameter disable_extra = False # disable --extra-args on --import if osimport: disable_extra = True is_import = uri.startswith("import") if is_import: # We use the special value 'import' for imagecreate.py. Since # it is connection agnostic, just let virt-install choose the # best hypervisor. uri = "" fullvirt = None is_xen = uri.startswith("xen") is_qemu = uri.startswith("qemu") if is_qemu: if virt_type != "kvm": fullvirt = True else: fullvirt = None # is libvirt new enough? if not utils.check_version_greater_or_equal(virtinst_version, "0.2.0"): raise InfoException( "need python-virtinst >= 0.2 or virt-install package to do installs for qemu/kvm (depending on your OS)") floppy = None cdrom = None location = None importpath = None if is_import: importpath = profile_data.get("file") if not importpath: raise InfoException("Profile 'file' required for image install") elif "file" in profile_data: if is_xen: raise InfoException("Xen does not work with --image yet") # this is an image based installation input_path = profile_data["file"] print("- using image location %s" % input_path) if input_path.find(":") == -1: # this is not an NFS path cdrom = input_path else: (tempdir, filename) = utils.nfsmount(input_path) cdrom = os.path.join(tempdir, filename) autoinst = profile_data.get("autoinst", "") if autoinst != "": # we have a (windows?) answer file we have to provide # to the ISO. print("I want to make a floppy for %s" % autoinst) floppy = utils.make_floppy(autoinst) elif is_qemu or is_xen: # images don't need to source this if "install_tree" not in profile_data: raise InfoException( "Cannot find install source in autoinst file, aborting.") if not profile_data["install_tree"].endswith("/"): profile_data["install_tree"] = profile_data["install_tree"] + "/" location = profile_data["install_tree"] disks = _sanitize_disks(disks) nics = _sanitize_nics(profile_data.get("interfaces"), bridge, profile_data.get("virt_bridge"), profile_data.get("network_count")) if not nics: # for --profile you get one NIC, go define a system if you want more. # FIXME: can mac still be sent on command line in this case? if bridge is None: bridge = profile_data["virt_bridge"] if bridge == "": raise InfoException( "virt-bridge setting is not defined in cobbler") nics = [(bridge, None)] kernel = profile_data.get("kernel_local") initrd = profile_data.get("initrd_local") breed = profile_data.get("breed") os_version = profile_data.get("os_version") if os_version and breed == "ubuntu": os_version = "ubuntu%s" % os_version if os_version and breed == "debian": os_version = "debian%s" % os_version net_model = None disk_bus = None machine_type = None if is_qemu: net_model = qemu_net_type disk_bus = qemu_driver_type machine_type = qemu_machine_type if machine_type is None: machine_type = "pc" cmd = "virt-install " if uri: cmd += "--connect %s " % uri cmd += "--name %s " % name cmd += "--ram %s " % ram cmd += "--vcpus %s " % vcpus if uuid: cmd += "--uuid %s " % uuid if virt_auto_boot and not disable_autostart: cmd += "--autostart " if gfx_type is None: cmd += "--nographics " else: cmd += "--graphics %s " % gfx_type if is_qemu and virt_type: if not disable_virt_type: cmd += "--virt-type %s " % virt_type if is_qemu and machine_type and not disable_machine_type: cmd += "--machine %s " % machine_type if fullvirt or is_qemu or is_import: if fullvirt is not None: cmd += "--hvm " elif oldstyle_accelerate: cmd += "--accelerate " if virt_pxe_boot: cmd += "--pxe " elif cdrom: cmd += "--cdrom %s " % cdrom elif location: cmd += "--location %s " % location if (is_qemu or is_xen) and extra and not virt_pxe_boot and not disable_extra: cmd += ("--extra-args=\"%s\" " % extra) elif importpath: cmd += "--import " import_exists = True if arch: cmd += "--arch %s " % arch else: cmd += "--paravirt " if not disable_boot_opt: cmd += ("--boot kernel=%s,initrd=%s,kernel_args=\"%s\" " % (kernel, initrd, extra)) else: if location: cmd += "--location %s " % location if extra: cmd += "--extra-args=\"%s\" " % extra if breed and breed != "other": if os_version and os_version != "other": if breed == "suse": suse_version_re = re.compile(r"^(opensuse[0-9]+)\.([0-9]+)$") if suse_version_re.match(os_version): os_version = suse_version_re.match(os_version).groups()[0] elif os_version == "generic26": os_version = "sles11" elif os_version.endswith("generic"): os_version = os_version.replace("generic", "") # make sure virt-install knows about our os_version, # otherwise default it to virtio26 or generic26 # found = False if os_version in supported_variants: pass # os_version is correct elif os_version + ".0" in supported_variants: # osinfo based virt-install only knows about major.minor # variants, not just major variants like it used to. Default # to major.0 variant in that case. Lack of backwards # compatibility in virt-install grumble grumble. os_version = os_version + ".0" else: os_version = "generic26" print("- warning: virt-install doesn't know this os_version, defaulting to %s" % os_version) cmd += "--os-variant %s " % os_version else: distro = "unix" if breed in ["debian", "suse", "redhat"]: distro = "linux" elif breed in ["windows"]: distro = "windows" cmd += "--os-type %s " % distro if importpath: # This needs to be the first disk for import to work cmd += "--disk path=%s " % importpath for path, size, driver_type in disks: print("- adding disk: %s of size %s (driver type=%s)" % (path, size, driver_type)) cmd += "--disk path=%s" % (path) if str(size) != "0": cmd += ",size=%s" % size if disk_bus: cmd += ",bus=%s" % disk_bus if driver_type and not disable_driver_type: cmd += ",format=%s" % driver_type cmd += " " if floppy: cmd += "--disk path=%s,device=floppy " % floppy for bridge, mac in nics: cmd += "--network bridge=%s" % bridge if net_model and not disable_net_model: cmd += ",model=%s" % net_model if mac: if oldstyle_macs: cmd += " --mac=%s" % mac else: cmd += ",mac=%s" % mac cmd += " " cmd += "--wait %d " % int(wait) if noreboot: cmd += "--noreboot " if osimport and not (import_exists): cmd += "--import " cmd += "--noautoconsole " return shlex.split(cmd.strip()) 0707010000003D000081ED00000000000000000000000160FFEB8C0000121B000000000000000000000000000000000000001D00000000koan-3.0.1/koan/vmwcreate.py""" Virtualization installation functions. Copyright 2007-2008 Red Hat, Inc and Others. Michael DeHaan <michael.dehaan AT gmail> 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 """ import os from .cexceptions import VirtCreateException, InfoException IMAGE_DIR = "/var/lib/vmware/images" VMX_DIR = "/var/lib/vmware/vmx" # FIXME: what to put for guestOS # FIXME: are other settings ok? TEMPLATE = """ #!/usr/bin/vmware config.version = "8" virtualHW.version = "4" numvcpus = "2" scsi0.present = "TRUE" scsi0.virtualDev = "lsilogic" scsi0:0.present = "TRUE" scsi0:0.writeThrough = "TRUE" ide1:0.present = "TRUE" ide1:0.deviceType = "cdrom-image" Ethernet0.present = "TRUE" Ethernet0.AddressType = "static" Ethernet0.Address = "%(MAC_ADDRESS)s" Ethernet0.virtualDev = "e1000" guestOS = "linux" priority.grabbed = "normal" priority.ungrabbed = "normal" powerType.powerOff = "hard" powerType.powerOn = "hard" powerType.suspend = "hard" powerType.reset = "hard" floppy0.present = "FALSE" scsi0:0.filename = "%(VMDK_IMAGE)s" displayName = "%(IMAGE_NAME)s" memsize = "%(MEMORY)s" """ # ide1:0.filename = "%(PATH_TO_ISO)s" def make_disk(disksize, image): cmd = "vmware-vdiskmanager -c -a lsilogic -s %sGb -t 0 %s" % ( disksize, image) print("- %s" % cmd) rc = os.system(cmd) if rc != 0: raise VirtCreateException("command failed") def make_vmx(path, vmdk_image, image_name, mac_address, memory): template_params = { "VMDK_IMAGE": vmdk_image, "IMAGE_NAME": image_name, "MAC_ADDRESS": mac_address.lower(), "MEMORY": memory } templated = TEMPLATE % template_params fd = open(path, "w+") fd.write(templated) fd.close() def register_vmx(vmx_file): cmd = "vmware-cmd -s register %s" % vmx_file print("- %s" % cmd) rc = os.system(cmd) if rc != 0: raise VirtCreateException("vmware registration failed") def start_vm(vmx_file): os.chmod(vmx_file, 0o755) cmd = "vmware-cmd %s start" % vmx_file print("- %s" % cmd) rc = os.system(cmd) if rc != 0: raise VirtCreateException("vm start failed") def start_install(name=None, ram=None, disks=None, mac=None, uuid=None, extra=None, vcpus=None, profile_data=None, arch=None, gfx_type=None, fullvirt=True, bridge=None, virt_type=None, virt_auto_boot=False, qemu_driver_type=None, qemu_net_type=None): if "file" in profile_data: raise InfoException("vmware does not work with --image yet") mac = None if "interfaces" not in profile_data: print("- vmware installation requires a system, not a profile") return 1 for iname in profile_data["interfaces"]: intf = profile_data["interfaces"][iname] mac = intf["mac_address"] if mac is None: print("- no MAC information available in this record, cannot install") return 1 print("DEBUG: name=%s" % name) print("DEBUG: ram=%s" % ram) print("DEBUG: mac=%s" % mac) print("DEBUG: disks=%s" % disks) # starts vmware using PXE. disk/mem info come from Cobbler # rest of the data comes from PXE which is also intended # to be managed by Cobbler. if not os.path.exists(IMAGE_DIR): os.makedirs(IMAGE_DIR) if not os.path.exists(VMX_DIR): os.makedirs(VMX_DIR) if len(disks) != 1: raise VirtCreateException( "vmware support is limited to 1 virtual disk") disksize = disks[0][1] image = "%s/%s" % (IMAGE_DIR, name) print("- saving virt disk image as %s" % image) make_disk(disksize, image) vmx = "%s/%s" % (VMX_DIR, name) print("- saving vmx file as %s" % vmx) make_vmx(vmx, image, name, mac, ram) register_vmx(vmx) start_vm(vmx) 0707010000003E000081ED00000000000000000000000160FFEB8C000005AB000000000000000000000000000000000000001D00000000koan-3.0.1/koan/xencreate.py""" Virtualization installation functions. Currently somewhat Xen/paravirt specific, will evolve later. Copyright 2006-2008 Red Hat, Inc and Others. Michael DeHaan <michael.dehaan AT gmail> Original version based on virtguest-install Jeremy Katz <katzj@redhat.com> Option handling added by Andrew Puch <apuch@redhat.com> Simplified for use as library by koan, Michael DeHaan <michael.dehaan AT gmail> 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 """ from . import utils from . import virtinstall def start_install(*args, **kwargs): cmd = virtinstall.build_commandline("xen:///", *args, **kwargs) rc, result, result_stderr = utils.subprocess_get_response(cmd, ignore_rc=True, get_stderr=True) if rc != 0: raise utils.InfoException("command failed (%s): %s %s" % (rc, result, result_stderr)) 0707010000003F000081A400000000000000000000000160FFEB8C000005CE000000000000000000000000000000000000001400000000koan-3.0.1/setup.py""" Copyright 2015 Jorgen Maas <jorgen.maas@gmail.com> This file is part of koan. Koan 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. Zenossctl 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 zenossctl. If not, see <http://www.gnu.org/licenses/>. """ from setuptools import setup VERSION = "3.0.1" setup( name='koan', version=VERSION, description='Kickstart over a Network Client for Cobbler', long_description='This client can initiate and prepare a reinstallation of your operation system with the help of' 'cobbler.', author='Team Cobbler', author_email='cobbler.project@gmail.com', url='https://www.github.com/cobbler/koan', packages=['koan'], license='GPLv2', scripts=['bin/koan', 'bin/cobbler-register'], install_requires=[ 'distro', 'libvirt-python', 'netifaces', ], extras_require={ 'lint': ['pyflakes', 'pycodestyle'], 'test': ['pytest', 'nose', 'pytest-cov', 'pytest-mock'], 'docs': ['Sphinx', 'sphinx-rtd-theme'] }, ) 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!488 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor