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&amp;utm_medium=referral&amp;utm_content=cobbler/koan&amp;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
openSUSE Build Service is sponsored by